Linux程序建立及同步實驗
阿新 • • 發佈:2019-02-12
用full、empty和mutex分別表示倉庫的庫存的同步訊號量、倉庫為空的同步訊號量和正在對倉庫進行操作的互斥訊號量。其初值分別為0、倉庫的容量(程式中使用MAX_BUFFRT_SIZE表示)和0。生產者:p(empty) -> p(mute) -> v(mutex) -> v(full) 消費者:p(full) -> p(mutex) -> v(mutex) ->v(empty)。
具體流程圖:
#include <stdio.h>
#include <stdlib.h> #include <unistd.h> #include <time.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/sem.h> #include <sys/types.h> #define MAX_BUFFER_SIZE 10 #define SHM_MODE 0600 #define SEM_MODE 0600 #define SEM_FULL 0 #define SEM_EMPTY 1 #define MUTEX 2 /* #if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED) // union semun is defined by including <sys/sem.h> #else // according to X/OPEN we have to define it ourselves union semun{ int val; struct semid_ds *buf; unsigned short *array; }; #endif union semun su;//sem union,用於初始化訊號量 */ struct my_buffer { int head; int tail; char str[MAX_BUFFER_SIZE]; int num; //緩衝區裡字母數量 int is_empty; }; const int N_CONSUMER = 2;//消費者數量 const int N_PRODUCER = 2;//生產者數量 const int N_BUFFER = 10;//緩衝區容量 const int N_WORKTIME = 10;//工作次數 int shm_id = -1; int sem_id = -1; pid_t child; pid_t parent; //得到10以內的一個隨機數 int get_random() { int digit; srand((unsigned)(getpid() + time(NULL))); digit = rand() % 10; return digit; } //得到A~Z的一個隨機字母 char getRandChar() { char letter; srand((unsigned)(getpid() + time(NULL))); letter = (char)((rand() % 26) + 'A'); return letter; } //sem_id 表示訊號量集合的 id //sem_num 表示要處理的訊號量在訊號量集合中的索引 //P操作 void waitSem(int sem_id,int sem_num) { struct sembuf sb; sb.sem_num = sem_num; sb.sem_op = -1;//表示要把訊號量減一 sb.sem_flg = SEM_UNDO;// //第二個引數是 sembuf [] 型別的,表示陣列 //第三個引數表示 第二個引數代表的陣列的大小 if(semop(sem_id,&sb,1) < 0){ perror("waitSem failed"); exit(1); } } //V操作 void sigSem(int sem_id,int sem_num) { struct sembuf sb; sb.sem_num = sem_num; sb.sem_op = 1; sb.sem_flg = SEM_UNDO; //第二個引數是 sembuf [] 型別的,表示陣列 //第三個引數表示 第二個引數代表的陣列的大小 if(semop(sem_id,&sb,1) < 0){ perror("sigSem failed"); exit(1); } } //列印程序執行結果 void printTime() { //列印時間 time_t now; struct tm *timenow; //例項化tm結構指標 time(&now); timenow = localtime(&now); printf("執行時間: %s ",asctime(timenow)); } int main(int argc, char ** argv) { shm_id = shmget(IPC_PRIVATE,MAX_BUFFER_SIZE,SHM_MODE); //申請共享記憶體 if(shm_id < 0) { perror("create shared memory failed"); exit(1); } struct my_buffer *shmptr; shmptr = shmat(shm_id, 0, 0); //將申請的共享記憶體附加到申請通訊的程序空間 if (shmptr == (void*)-1) { perror("add buffer to using process space failed!\n"); exit(1); } if((sem_id = semget(IPC_PRIVATE,3,SEM_MODE)) < 0) { //建立三個訊號量,SEM_EMPTY,SEM_FULL和MUTEX perror("create semaphore failed! \n"); exit(1); } if(semctl(sem_id,SEM_FULL,SETVAL,0) == -1) { //將索引為0的訊號量設定為0-->SEM_FULL perror("sem set value error! \n"); exit(1); } if(semctl(sem_id,SEM_EMPTY,SETVAL,10) == -1) { //將索引為1的訊號量設定為10-->SEM_EMPTY perror("sem set value error! \n"); exit(1); } if(semctl(sem_id,MUTEX,SETVAL,1) == -1) { //將索引為3的訊號量設定為1-->MUTEX perror("sem set value error! \n"); exit(1); } shmptr -> head = 0; shmptr -> tail = 0; shmptr -> is_empty = 1; shmptr -> num = 0; for(int i = 0; i < N_PRODUCER; i++) { parent = fork(); if(parent < 0) { perror("the fork failed"); exit(1); } else if(parent == 0) { shmptr = shmat(shm_id, 0, 0); //將申請的共享記憶體附加到申請通訊的程序空間 if (shmptr == (void*)-1) { perror("add buffer to using process space failed!\n"); exit(1); } int count = 0; for(int j = 0; j < N_WORKTIME; j++) { waitSem(sem_id, SEM_EMPTY); waitSem(sem_id, MUTEX); sleep(get_random()); printf("-------------------------------------------------------------\n"); printf("我是第 %d 個生產者程序,PID = %d\n", i + 1, getpid()); /*生產產品*/ char c = getRandChar(); //隨機獲取字母 shmptr -> str[shmptr->tail] = c; shmptr -> tail = (shmptr->tail + 1) % MAX_BUFFER_SIZE; shmptr -> is_empty = 0; //寫入新產品 shmptr -> num++; /*列印輸出結果*/ printTime(); //程式執行時間 int p; printf("緩衝區資料(%d個):",shmptr -> num); //列印緩衝區中的資料 p = (shmptr->tail-1 >= shmptr->head) ? (shmptr->tail-1) : (shmptr->tail-1 + MAX_BUFFER_SIZE); for (p; !(shmptr -> is_empty) && p >= shmptr -> head; p--) { printf("%c", shmptr -> str[p % MAX_BUFFER_SIZE]); } printf("\t 生產者 %d 放入 '%c'. \n", i + 1, c); printf("-------------------------------------------------------------\n"); fflush(stdout); sigSem(sem_id, MUTEX); sigSem(sem_id, SEM_FULL); } //將共享段與程序之間解除連線 shmdt(shmptr); exit(0); } } for(int i = 0; i < N_CONSUMER; i++) { child = fork(); if(child < 0)//呼叫fork失敗 { perror("the fork failed"); exit(1); } else if(child == 0) { int count = 0; shmptr = shmat(shm_id, 0, 0); //將申請的共享記憶體附加到申請通訊的程序空間 if (shmptr == (void*)-1) { perror("add buffer to using process space failed!\n"); exit(1); } for(int j = 0; j < N_WORKTIME; j++) { waitSem(sem_id, SEM_FULL); waitSem(sem_id, MUTEX); sleep(get_random()); printf("-------------------------------------------------------------\n"); printf("我是第 %d 個消費者程序,PID = %d\n", i + 1, getpid()); /*消費資料*/ char lt = shmptr -> str[shmptr -> head]; shmptr -> head = (shmptr -> head + 1) % MAX_BUFFER_SIZE; shmptr -> is_empty = (shmptr->head == shmptr->tail); // shmptr -> num--; /*列印輸出結果*/ printTime(); //程式執行時間 int p; printf("緩衝區資料(%d個):",shmptr -> num); //列印緩衝區中的資料 p = (shmptr -> tail - 1 >= shmptr -> head) ? (shmptr -> tail-1) : (shmptr -> tail - 1 + MAX_BUFFER_SIZE); for (p; !(shmptr -> is_empty) && p >= shmptr -> head; p--) { printf("%c", shmptr -> str[p % MAX_BUFFER_SIZE]); } printf("\t 消費者 %d 取出 '%c'. \n", i + 1, lt); printf("-------------------------------------------------------------\n"); fflush(stdout); sigSem(sem_id,MUTEX); sigSem(sem_id,SEM_EMPTY); } //將共享段與程序之間解除連線 shmdt(shmptr); exit(0); } } //主程序最後退出 while (wait(0) != -1); //將共享段與程序之間解除連線 shmdt(shmptr); //對共享記憶體區執行控制操作 shmctl(shm_id,IPC_RMID,0);//當cmd為IPC_RMID時,刪除該共享段 shmctl(sem_id,IPC_RMID,0); printf("主程序執行結束!\n"); fflush(stdout); exit(0); return 0; }
最終執行結果:
以上就是4個綜合題目的具體實現,其實每個題目都不復雜,只要搞懂了原理,耐住性子還是可以做出來的。不懂的話就要記得多查資料,努力去理解,希望每一個人都可以體會到Linux的別樣美!