linux进程间通信:system V 信号量 生产者和消费者模型编程案例
                                                            生活随笔
收集整理的這篇文章主要介紹了
                                linux进程间通信:system V 信号量 生产者和消费者模型编程案例
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.                        
                                生產者和消費者模型:
- 有若干個緩沖區,生產者不斷向里填數據,消費者不斷從中取數據
 - 兩者不沖突的前提: 
- 緩沖區有若干個,且是固定大小,生產者和消費者各有若干個
 - 生產者向緩沖區中填數據前需要判斷緩沖區是否滿,滿了則等待,直到有空間
 - 消費者從緩沖區中取數據前需要判斷緩沖區是否為空,空了則等待,直到緩沖區有數據
 - 在某一時刻,緩沖區中只允許有一個操作者進行讀或者寫操作
 
 
通過信號量,則可以將以上提出的不沖突的前提一一解決。當信號量的value值為0時,則對當前信號量進行p操作(-1)時當前進程會掛起,直到信號量的value值為1。
當前生產者消費者模型并不友好,即生產者和消費者都需要受災人口緩沖區前等待,判斷是否能夠寫入或者取出,極大得浪費了系統空間。后期的優化應該為,當生產者寫滿之后向消費者發送一個可以來取信息的信號之后就離開,去做自己的事情。而消費者取完之后發送一個信號告訴生產者取完了,可以繼續寫入,消費者即可回去做自己的事情。
 后期的優化待了解了進程信號處理方式之后合入當前生產者消費者模型之中
編程實例
- 2個生產者每隔1秒向緩沖區中寫入一次數據
 - 3個消費者每3秒,3秒,5秒從緩沖區中讀出數據
 - 2個生產者分別對寫信號量做P操作,對讀信號量做V操作
 - 3個消費者分別對寫信號量做V操作,對讀信號量做P操作
 
代碼如下:
 productor.c生產者
#include<stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>//向獲取到達共享內存的地址中寫入值為data 的數據
//使用index表示當前數組下標同時表示當前緩沖區有多少個元素
//隨著數據的不斷寫入,下標不斷增加
void mem_write(int *addr, int data) {int index;index = addr[0];index ++;addr[index] = data;addr[0] = index;
}union semnum {int val;struct semid_ds *buf;unsigned short int *array;struct seminfo *__buf;
};
int sem_id;//初始化信號量的編號以及信號量的value值
void sem_init(int semid, int nsignum, int sem_value) {union semnum sem_union;sem_union.val = sem_value;if (semctl(semid,nsignum,SETVAL,sem_union) == -1) {printf("semctl failed\n");_exit(-1);}
}//信號量-1操作,即編號為nsignum的信號量的value進行-1操作
void sem_p(int semid, int nsignum) {struct sembuf sops;sops.sem_num = nsignum;sops.sem_op = -1;sops.sem_flg = SEM_UNDO;if (semop(sem_id, &sops,1) == -1) {printf("semop P failed \n");_exit(-1);}}//信號量+1 操作,即編號為nsignum的信號量的value進行+1操作
void sem_v(int sem_id, int nsignum) {struct sembuf sops;sops.sem_num = nsignum;sops.sem_op = 1;sops.sem_flg = SEM_UNDO;if (semop(sem_id, &sops,1) == -1) {printf("semop V failed \n");_exit(-1);}
}//打印信號量,通過semctl獲取信號量的數據結構,暫時沒有用到
void sem_print(int sem_id, int nsignum) {int sem_value;sem_value = semctl(sem_id, nsignum, GETVAL);printf("sem[%d] = %d \n",nsignum, sem_value);
}int main() {//生成共享內存和信號量的keyint shm_id;key_t shm_key = ftok("./",111);key_t sem_key = ftok("./",112);//shmget獲取共享內存的IPC標識,并進行內存的映射shmat得到共享內存的地址shm_id = shmget(shm_key, 1028, IPC_CREAT|0666);char *shm_addr = shmat(shm_id,NULL,0);//memset (shm_adr,0,sizeof(shm_addr));memset (shm_addr,0,128);//通過key生成信號量的IPC標識sem_id = semget(sem_key, 2,IPC_CREAT | 0666);if (sem_id == -1) {printf("semget failed\n");_exit(-1);} else {sem_init(sem_id,0,0); //read sem//寫信號量初始化為5,表示可以寫5個緩沖區,即我們上圖中描述的支持寫入5個bufsem_init(sem_id,1,5); //write sem}int ret;if( (ret = fork()) == -1 ) {printf("fork failed\n");_exit(-1);}//創建子進程,每隔一秒,寫入數據else if (ret == 0) {int child_data = 1;while (1) {sleep(1);//寫入之前將寫信號量進行p操作(-1),表示寫入一個緩沖區sem_p(sem_id,1);printf("child data  %d\n",child_data);mem_write((int *)shm_addr,child_data);child_data += 2;//寫之后將讀信號量進行v操作(+1),表示支持讀一個緩沖區sem_v(sem_id,0);}}//父進程每隔一秒寫入數據else {int parent_data = 2;while(1) {sleep(1);sem_p(sem_id ,1);printf("parent_data %d\n",parent_data);mem_write((int *)shm_addr, parent_data);parent_data = parent_data + 2;sem_v(sem_id,0);}}
}
 
consumer.c消費者
#include<stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>//這里讀時和mem_write相反,此時從共享內存中讀出一個數據
//同時將index--;表示讀出了一個數據,已經支持向內存寫入
int mem_read(int *addr) {int index, data;index = addr[0];data = addr[index];index --;addr[0] = index;return data;
}union semnum {int val;struct semid_ds *buf;unsigned short int *array;struct seminfo *__buf;
};
int sem_id;void sem_init(int semid, int nsignum, int sem_value) {union semnum sem_union;sem_union.val = sem_value;if (semctl(semid,nsignum,SETVAL,sem_union) == -1) {printf("semctl failed\n");_exit(-1);}
}void sem_p(int semid, int nsignum) {struct sembuf sops;sops.sem_num = nsignum;sops.sem_op = -1;sops.sem_flg = SEM_UNDO;if (semop(sem_id, &sops,1) == -1) {//printf("semop P failed \n");//_exit(-1);}}void sem_v(int sem_id, int nsignum) {struct sembuf sops;sops.sem_num = nsignum;sops.sem_op = 1;sops.sem_flg = SEM_UNDO;if (semop(sem_id, &sops,1) == -1) {//printf("semop v failed \n");// _exit(-1);}
}void sem_print(int sem_id, int nsignum) {int sem_value;sem_value = semctl(sem_id, nsignum, GETVAL);printf("sem[%d] = %d \n",nsignum, sem_value);
}int main() {//共享內存和信號量都支持key形式的對象生成int shm_id,sem_id;key_t shm_key = ftok("./",111);key_t sem_key = ftok("./",112);//獲取共享內存的IPC標識,并進行內存的映射得到共享內存的地址shm_id = shmget(shm_key, 1028, IPC_CREAT|0666);char *shm_addr = shmat(shm_id,NULL,0);memset (shm_addr,0,128);//通過key生成信號量的IPC標識sem_id = semget(sem_key, 2,IPC_CREAT | 0666);if (sem_id == -1) {printf("semget failed\n");_exit(-1);} else {sem_init(sem_id,0,0); //read semsem_init(sem_id,1,5); //write sem}//創建兩個子進程讀,每隔三秒讀一個緩沖區//讀之前將讀信號量p(-1)操作,表示讀一個信號量//讀之后將寫信號量v(+1)操作,表示支持寫一個信號量for (int i = 0;i < 2; ++i) {int ret;if ((ret = fork()) == -1) {printf("fork failed\n");_exit(-1);} else if (ret == 0) {while (1) {sleep(3);sem_p (sem_id, 0);printf("pid %d data :%d\n",getpid(), mem_read((int *)shm_addr));sem_v (sem_id,1);}}}//父進程同樣的方式也進行讀while(1) {sleep(5);sem_p(sem_id , 0);printf("pid %d data :%d\n",getpid(), mem_read((int *)shm_addr));sem_v(sem_id, 1);}return 0;
}
 
運行結果如下:
 
system V 信號量的通信特點
- 信號量是通過標識而不是常用的文件描述符來引用的
 - 使用鍵而不是文件名來表示信號量
 - 創建,初始化,操作信號量需要單獨的系統調用,分別為semget,semctl,semop;關于system V 信號量的詳細使用可以參考文章linux進程間通信:system V 信號量
 - 信號量的操作同樣會有阻塞現象。當信號量的value值為0時,則對當前信號量進行p操作(-1)時當前進程會掛起,直到信號量的value值為1。
 
總結
以上是生活随笔為你收集整理的linux进程间通信:system V 信号量 生产者和消费者模型编程案例的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: linux进程间通信:system V
 - 下一篇: 求一个女生qq网名伤感霸气