信号量的实现和应用

来源:互联网 发布:程序员很难找女朋友吗 编辑:程序博客网 时间:2024/05/17 02:31

今天做操作系统实验真是相当的纠结。。当别人都在队列什么的讨论的时候。。我的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;}

大体就是这些问题,感觉对于内核的了解还是太欠缺了,有的地方都是凭着感觉改的,也不知道什么原因会对会错的,内核的代码还是有待研究啊~

原创粉丝点击