本次實驗的基本內容是: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個數字。