本次实验的基本内容是:linux
10: 0 10: 1 10: 2 10: 3 10: 4 11: 5 11: 6 12: 7 10: 8 12: 9 12: 10 12: 11 12: 12 …… 11: 498 11: 499
其中 ID 的顺序会有较大变化,但冒号后的数字必定是从 0 开始递增长一的。
pc.c 中将会用到 sem_open() 、 sem_close() 、 sem_wait() 和 sem_post() 等信号量相关的系统调用,请查阅相关文档。
《UNIX环境高级编程》是一本关于 Unix/Linux 系统级编程的至关经典的教程。 电子版可在网站上下载,后续实验也用获得。若是你对 POSIX 编程感兴趣,建议买一本常备手边。编程
Linux 在 0.11 版尚未实现信号量,Linus 把这件富有挑战的工做留给了你。 若是能实现一套山寨版的彻底符合 POSIX 规范的信号量,无疑是颇有成就感的。但时间暂时不容许咱们这么作,因此先弄一套缩水版的类 POSIX 信号量,它的函数原型和标准并不彻底相同,并且只包含以下系统调用:缓存
sem_t *sem_open(const char *name, unsigned int value); int sem_wait(sem_t *sem); int sem_post(sem_t *sem); int sem_unlink(const char *name);
sem_t
是信号量类型,根据实现的须要自定义。
sem_open()
的功能是建立一个信号量,或打开一个已经存在的信号量。
name
是信号量的名字。不一样的进程能够经过提供一样的name
而共享同一个信号量。若是该信号量不存在,就建立新的名为name
的信号量;若是存在,就打开已经存在的名为name
的信号量。value
是信号量的初值,仅当新建信号量时,此参数才有效,其他状况下它被忽略。当成功时,返回值是该信号量的惟一标识(好比,在内核的地址、ID
等),由另两个系统调用使用。如失败,返回值是NULL
。sem_wait()
就是信号量的P
原子操做。若是继续运行的条件不知足,则令调用进程等待在信号量sem
上。返回0
表示成功,返回-1
表示失败。
sem_post()
就是信号量的V
原子操做。若是有等待sem
的进程,它会唤醒其中的一个。返回0
表示成功,返回-1
表示失败。
sem_unlink()
的功能是删除名为name
的信号量。返回0
表示成功,返回-1
表示失败。
在kernel
目录下新建sem.c文件实现如上功能。而后将pc.c从Ubuntu移植到0.11下,测试本身实现的信号量。函数
Producer() { P(Mutex); //互斥信号量 // 生产一个产品item; P(Empty); //空闲缓存资源 // 将item放到空闲缓存中; V(Full); //产品资源 V(Mutex); } Consumer() { P(Mutex); P(Full); // 从缓存区取出一个赋值给item; V(Empty); // 消费产品item; V(Mutex); }
在实验过程当中,咱们实现的多个函数都要以系统调用的形式在Linux0.11中进行使用,所以,须要根据实验2的内容对makefile、unistd.h、system_call.s等文件进行修改,修改的过程不在此赘述,能够参考另外一篇讲实验2的博客。post
sem_t *sys_sem_open(const char *name,unsigned int value) //sem_t的定义会在后边给出,name是信号量的名称,value信号量对应的初值 { char kernelname[100]; int isExist = 0; int i = 0; int name_cnt = 0; while(get_fs_byte(name + name_cnt) != '\0')//进行信号量名称长度的读取 { name_cnt++; } if(name_cnt > SEM_NAME_LEN)//信号量名称需小于最大长度 { return NULL; } for(i = 0;i < name_cnt;i++)//将信号量的名称读入 { kernelname[i] = get_fs_byte(name + i); } int name_len = strlen(kernelname); int sem_name_len =0; sem_t *p = NULL; for(i = 0;i < cnt;i++)//判断是否当前信号已经存在 { sem_name_len = strlen(semtable[i].name); if(sem_name_len == name_len) { if( !strcmp(kernelname,semtable[i].name) ) { isExist = 1; break; } } } if(isExist == 1)//若是已经存在,那么返回已经存在的信号 { p = (sem_t*)(&semtable[i]); } else//不然新建一个信号 { i = 0; for(i = 0;i < name_len;i++) { semtable[cnt].name[i] = kernelname[i]; } semtable[cnt].value = value; p = (sem_t*)(&semtable[cnt]); cnt++;//而且将这个信号放入信号表中 } return p; }
这个函数实现的是打开信号量的操做。测试
int sys_sem_wait(sem_t *sem) { cli();//关中断 while(sem->value <= 0) //进程等待直到信号量的值大于0 { sleep_on(&(sem->queue)); } sem->value--; sti();//开启中断 return 0; }
这个函数实现了进程的等待过程。网站
int sys_sem_post(sem_t *sem) { cli(); sem->value++; if((sem->value) <= 1)//唤醒在信号量上等待的进程 { wake_up(&(sem->queue)); } sti(); return 0; }
这个函数用于唤醒对应的进程。设计
int sys_sem_unlink(const char *name) { char kernelname[100]; int isExist = 0; int i = 0; int name_cnt = 0; while( get_fs_byte(name + name_cnt) != '\0') { name_cnt++; } if(name_cnt > SEM_NAME_LEN) { return NULL; } for(i=0;i<name_cnt;i++) { kernelname[i] = get_fs_byte(name + i); } int name_len = strlen(name); int sem_name_len =0; for(i = 0;i < cnt;i++) { sem_name_len = strlen(semtable[i].name); if(sem_name_len == name_len) { if( !strcmp(kernelname,semtable[i].name)) { isExist = 1; break; } } } if(isExist == 1) { int tmp = 0; for(tmp = i;tmp <= cnt;tmp++) { semtable[tmp] = semtable[tmp + 1]; } cnt = cnt - 1; return 0; } else return -1; }
这个函数用于删除名为name的信号量。指针
这个文件定义了sem_t这个数据类型。code
#ifndef _SEM_H #define _SEM_H #include <linux/sched.h> #define SEM_NAME_LEN 50 #define SEMTABLE_LEN 20 typedef struct sem_t{ char name[SEM_NAME_LEN];//信号量名称 unsigned int value;//初值 struct task_struct *queue;//等待信号量的进程指针 }sem_t; extern sem_t semtable[SEMTABLE_LEN]; sem_t *sem_open(const char name, unsigned int value); int sem_wait(sem_t *sem); int sem_post(sem_t *sem); int sem_unlink(const char *name); #endif
#define __LIBRARY__ #include <unistd.h> #include <stdio.h> #include <linux/sem.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> _syscall2(sem_t*, sem_open, const char*, name, unsigned int, value) _syscall1(int, sem_wait, sem_t *, sem) _syscall1(int, sem_post, sem_t *, sem) _syscall1(int, sem_unlink, const char *, name) #define NUMBUFFER 550 /*打出数字的总数*/ #define NUMPRO 5 /*消费者进程个数*/ #define MAXSIZE 10 /*缓冲区大小*/ sem_t *empty, *full, *mutex; int main() { int i, j, k; FILE *fp = NULL; int buf_out = 0;/*从缓冲区读取的位置*/ int buf_in = 0;/*写入缓冲区的位置*/ empty = (sem_t *)sem_open("EMPTY", MAXSIZE);/*打开对应的信号量*/ full = (sem_t *)sem_open("FULL", 0); mutex = (sem_t *)sem_open("MUTEX", 1); fp = fopen("/var/buffer.dat", "wb+"); /*存在文件中。*/ fseek(fp, MAXSIZE * sizeof(int), SEEK_SET); fwrite(&buf_out, 1, sizeof(buf_out), fp); fflush(fp); if(!fork()) { for(i = 0;i < NUMBUFFER;i ++) { sem_wait(empty); sem_wait(mutex); fseek(fp, buf_in * sizeof(int), SEEK_SET); fwrite(&i, 1, sizeof(i), fp); fflush(fp); buf_in = (buf_in + 1) % MAXSIZE; sem_post(mutex); sem_post(full); } return 0; } for(i = 0;i < NUMPRO; i++) { if(!fork()) { for(j = 0;j < NUMBUFFER / NUMPRO; j++) { int cost; sem_wait(full); sem_wait(mutex); fflush(stdout); fseek(fp, MAXSIZE * sizeof(int), SEEK_SET); fread(&buf_out, sizeof(int), 1, fp); fseek(fp, buf_out * sizeof(int), SEEK_SET); fread(&cost, sizeof(int), 1, fp); printf("%d:\t%d\n", getpid(), cost); fflush(stdout); buf_out = (buf_out + 1) % MAXSIZE; fseek(fp, MAXSIZE * sizeof(int), SEEK_SET); fwrite(&buf_out, 1, sizeof(buf_out),fp); fflush(fp); sem_post(mutex); sem_post(empty); } return 0; } } wait(NULL); sem_unlink("EMPTY"); sem_unlink("FULL"); sem_unlink("MUTEX"); fclose(fp); return 0; }
.... 17: 543 17: 544 17: 545 17: 546 17: 547 17: 548 17: 549
最终能够输出550个数字。