今天做操作系统实验真是相当的纠结。。当别人都在队列什么的讨论的时候。。我的pc.c始终不给力。。现将遇到的问题总结如下,避免以后再犯~
首先声明一下,我用的是系统调用的方法。真心不知道为什么大家都不使用推荐的系统调用唉。。看来我还是比较听话的嘿嘿~
1、刚开始运行的时候总是会出现死循环一样的死锁状,让我极端郁闷。。后来无意中改了信号量的名字就好了,让我一直以为是信号量的名字和该指针的名字不能一致,还在人人咆哮了一番。。实在是好弱智啊。。其实是自己忘记unlink了。。信号量可不会在你程序退出的时候自动消失哦~所以用unlink把它从系统中kill掉~否则就会出现可怕的
“死锁状”。。。所以说,现在在ubuntu下编程的思路可要改一改,不能总是那种单线程顺序执行的思路,还有操作了系统中的东西,也用了信号量,系统调用什么的,有的东西实在系统中一直存在的,要养成用完了还回去的好习惯,发扬风度嘛~就像yield()...
2、文件读写指针问题也浪费了一点时间,忘记了读写指针其实是一个指针,你读过之后指针的位置也变了,再写的时候指针的位置都要注意啦~
3、我删除第一个数据的时候用的笨笨的方法,和那次java的ATM机一样唉,真不知道为什么不能发明文件行删除的函数。。。我就是读完第一个数据打印出来之后就把后面的都读入数组,然后在写一遍。。BUT。。忘记写O_TRUNC了,导致文件没有覆盖,就超级超级长。。在此感谢liushuaikobe同学的debug功力。。。另外一定要在死循环里面写文件的打开和关闭,不然可就乱套了。。。
4、后来发现文件不存在的时候会一直打印0出来,根本就没有生产嘛。。原来我用的是read的返回值是不是等于零来判断文件是否存在,但是不知到为什么会一直调用,也没有写入文件,我就上网搜到了access函数。access("product",F_OK)的返回值是-1说明不存在,而且它不止可以判断文件是否存在,还能判断是否可读啦 ,是否可写啦等等等等,以后可能用的到~刚开始我写在producer的函数里面了,但是主函数里一开始就创建了,所以永远存在的,让我还怀疑了access函数的正确性,实在是不好意思。。。后来传参进producer里面,并且要记得写完之后讲flag置为1哦~不然还是一直是0。。
5、还有一个非常弱智的问题也要引起重视。。就是后来程序运行的时候子进程号(PS:用getpid()函数来获得当前的进程号,网上说如果返回负数则是fork()不成功,但是我修改了打开关闭文件那部分的操作就好了唉。。)总是一个,后来发现是把父进程写在了for循环里面,这样每次切换到父进程又会调用一边producer(),生产者就有好多了。。并且producer一定是在fork之后在调用,否则就死循环了。。。正确代码如下:
for(i=0; i<5; i++)
{
id = fork();
if(id==0)
{
consumer();
return 0;
}
}
producer();
接下来就是最费劲的部分了,要自己实现信号量的那四个系统调用,比pc.c遇到的问题还要纠结。。在此要感谢a578559967同学的专业debug精神。。职业debuger非他莫属了。。OK。。还是总结我遇到的问题吧。。以后要引以为戒。。
1、刚开始把sem_t 和queue的结构体都定义在了unistd.h文件里没错,但是位置搞错了。。写在了_syscall3的下面,总是说找不到,后来发现_syscall3那一堆东西前面有一个#ifdef __LIBRARY__ 才会define那些东西,所以就定义在了最后那个#endif的前面。。。
代码如下:
typedef struct Queue
{
struct task_struct* point;
struct Queue *next;
}queue;
typedef struct Sem_t
{
char name[20];
int value;
queue *head;
queue *tail;
}sem_t;
2、还有int sem_num和信号量数组,并不能写在unistd.h中,那里是声明类型还有宏定义的地方,刚开始写在那里出错,后来移动到了sem.c中。。。用户并不需要关心这两个东西,pc.c也用不到,应当写在内核中。
附上sem.c的代码,将其中的问题写在注释中。。。
#include <unistd.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <asm/segment.h>
#include <asm/system.h>
int sem_num=0; //记录信号量的个数
sem_t* semaphore[64]; //存放信号量指针的数组,便于unlink删除
sem_t* sys_sem_open(const char *name, unsigned int value)
{
sem_t *tmp;
int i;
cli();
/*****************************************************************
关键的关键!!下面这行语句自然而然想到的是malloc,这也是
我们烂熟于心的东西,但是伟大的郭勇老师说malloc会有问题,
不安全具体什么问题我也不清楚,不过我偷偷的剽窃了一下fork.c
和exit.c的代码,找到了两个不错的东西,用get_free_page()
代替malloc,用free_page代替free,之前遇到的后来一直是一个
进程在消费的问题就解决了~~
*****************************************************************/
tmp = (sem_t*)get_free_page();//malloc(sizeof(sem_t));
for(i=0;(tmp->name[i]=get_fs_byte(name+i))!='\0';i++); //信号量的名字要从用户态传到内核态并赋值给tmp->name
//所以使用第二次实验用到的get_fs_byte实现赋值
tmp->value = value;
tmp->head = NULL;
tmp->tail = NULL;
semaphore[sem_num]=tmp;
sem_num++;
sti();
return tmp;
}
int sys_sem_wait(sem_t *sem)
{
queue *tmp=NULL;
if(!sem)
{
return -1;
}
cli();
sem->value--;
if(sem->value<0)
{
tmp = (queue*)get_free_page();//malloc(sizeof(queue));
tmp->next = NULL;
if(sem->head==NULL)
{
sem->head = tmp;
sem->tail = tmp;
}
else
{
sem->tail->next = tmp;
sem->tail = tmp;
}
//printk("sleep %d\n",sys_getpid());
sleep_on(&tmp->point); //sleep_on的参数是struct task_struct**,需要传入queue中的point,刚开始忘记写point了。。
}
//刚开始在这个位置free了tmp,而此时tmp不一定被申请了,所以没有必要,会造成什么什么panic的错误。。。
sti();
return 0;
}
int sys_sem_post(sem_t *sem)
{
if(!sem)
{
return -1;
}
cli();
sem->value++;
//printk("post %s %d\n",sem->name,sem->value);
if(sem->value<=0)
{
if(sem->head != NULL)
{
queue *p = sem->head;
//printk("wake %d\n",sem->head->point->pid);
wake_up(&sem->head->point);
sem->head = sem->head->next;
free_page((long)p); //同malloc原理一样,若只改malloc会报错,free_page和get_free_page配套使用
}
}
sti();
return 0;
}
int sys_sem_unlink(const char *name)
{
cli();
int i;
for(i=0; i<sem_num; i++)
{
if(semaphore[i] != NULL && strcmp(semaphore[i]->name,name)==0)
{
free_page((long)semaphore[i]);
break;
}
}
sti();
return i==sem_num?-1:0;
}
大体就是这些问题,感觉对于内核的了解还是太欠缺了,有的地方都是凭着感觉改的,也不知道什么原因会对会错的,内核的代码还是有待研究啊~