【原创】《Linux高级程序设计》杨宗德著 - 进程管理与程序开发 - 信号量通信机制
信号量通信机制概念图
通常所说的创建一个信号量实际上是创建了一个信号量集合,在这个信号量集合中,可能有多个信号量,整个信号量集合由以下部分组成。
信号量集合数据结构
每一个信号量结构
Linux信号量管理操作
1. 创建信号量集合
2. 控制信号量集合、信号量
读取设置信号量集合的示例:
#include <stdio.h> #include <sys/types.h> #include <sys/sem.h> #include <errno.h> #define MAX_SEMAPHORES 5 int main(int argc,char *argv[]) { int i, ret, semid; unsigned short sem_array[MAX_SEMAPHORES]; unsigned short sem_read_array[MAX_SEMAPHORES]; union semun { int val; struct semid_ds *buf; unsigned short *array; } arg; semid = semget( IPC_PRIVATE, MAX_SEMAPHORES,IPC_CREAT | 0666 ); if (semid != -1) { /* Initialize the sem_array */ for ( i = 0 ; i < MAX_SEMAPHORES ; i++ ) { sem_array[i] = (unsigned short)(i+1); } /* Update the arg union with the sem_array address */ arg.array = sem_array; /* Set the values of the semaphore-array */ ret = semctl( semid, 0, SETALL, arg); if (ret == -1) printf("SETALL failed (%d)\n", errno); /* Update the arg union with another array for read */ arg.array = sem_read_array; /* Read the values of the semaphore array */ ret = semctl( semid, 0, GETALL, arg ); if (ret == -1) printf("GETALL failed (%d)\n", errno); /* print the sem_read_array */ for ( i = 0 ; i < MAX_SEMAPHORES ; i++ ) { printf("Semaphore %d, value %d\n", i, sem_read_array[i] ); } /* Use GETVAL in a similar manner */ for ( i = 0 ; i < MAX_SEMAPHORES ; i++ ) { ret = semctl( semid, i, GETVAL ); printf("Semaphore %d, value %d\n", i, ret ); } /* Delete the semaphore */ ret = semctl( semid, 0, IPC_RMID ); } else printf("Could not allocate semaphore (%d)\n", errno); return 0; }运行结果:
$ ./sem_get_value Semaphore 0, value 1 Semaphore 1, value 2 Semaphore 2, value 3 Semaphore 3, value 4 Semaphore 4, value 5 Semaphore 0, value 1 Semaphore 1, value 2 Semaphore 2, value 3 Semaphore 3, value 4 Semaphore 4, value 5
3. 信号量操作
sem_flg的可选值有:IPC_NOWAIT和 SEM_UNDO。
SEM_UNDO的应用示例:
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> static int semaphore_p(void); static int semaphore_v(void); static int set_semvalue(void); static int get_semvalue(void); union semun { int val; /* value for SETVAL */ struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */ unsigned short int *array; /* array for GETALL, SETALL */ struct seminfo *__buf; /* buffer for IPC_INFO */ }; int sem_id; int main(int argc,char *argv) { pid_t pid; int i; int value; key_t key; int status; if((pid=fork())==-1) { perror("fork"); exit(EXIT_FAILURE); } else if(pid==0) { if((sem_id=semget((key_t)123456,1,IPC_CREAT|0770))==-1) { perror("semget"); exit(EXIT_FAILURE); } if (!set_semvalue()) { fprintf(stderr, "Failed to initialize semaphore\n"); exit(EXIT_FAILURE); } value=get_semvalue(); printf("this is child,the current value is %d\n",value); if(!semaphore_v()) { fprintf(stderr, "Failed to v operator\n"); exit(EXIT_FAILURE); } value=get_semvalue(); printf("the child %d V operator,value=%d\n",i,value); printf("child exit success\n"); exit(EXIT_SUCCESS); } else //parent { sleep(3); if((sem_id=semget((key_t)123456,1,IPC_CREAT|0770))==-1) { perror("semget"); exit(EXIT_FAILURE); } value=get_semvalue(); printf("this is parent ,the current value is %d\n",value); printf("the parent will remove the sem\n"); if(semctl(sem_id,0, IPC_RMID,(struct msquid_ds*)0)==-1) { perror("semctl"); exit(EXIT_FAILURE); } return 0; } } static int set_semvalue(void) { union semun sem_union; int value; sem_union.val = 5; if (semctl(sem_id, 0, SETVAL, sem_union) == -1) return(0); printf("set value success,"); printf("init value is %d\n",get_semvalue()); return(1); } static int get_semvalue(void) { int res; if((res=semctl(sem_id, 0, GETVAL)) == -1) { perror("semctl"); exit(EXIT_FAILURE); } return res; } static int semaphore_v(void) { struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_op = 1; /* V() */ //sem_b.sem_flg = SEM_UNDO; sem_b.sem_flg=0; if (semop(sem_id, &sem_b, 1) == -1) { perror("semop"); return(0); } return(1); }
使用SEM_UNDO参数时的运行结果:
$ ./semop_undo_test set value success,init value is 5 this is child,the current value is 5 the child 0 V operator,value=6 child exit success this is parent ,the current value is 5 the parent will remove the sem不使用SEM_UNDO参数时的运行结果:
$ ./semop_undo_test set value success,init value is 5 this is child,the current value is 5 the child 0 V operator,value=6 child exit success this is parent ,the current value is 6 the parent will remove the sem
使用信号量实现生产消费问题
生产消费问题是一个经典的数学问题,要求生产者-消费者在固定的仓库空间条件下,生产者每生产一个产品将占用一个仓库空间,生产者生产的产品库存不能越过仓库的存储量,消费者每消费一个产品将增加一个仓库空间,消费者在仓库产品为0时不能再消费。
本例中采用信号量来解决这个问题,为了便于理解,本例中使用了两个信号量,一个用来管理消费者(以下为sem_produce),一个用来管理生产者(以下为sem_custom),即sem_produce表示当前仓库可用空间的数量,sem_custom用来表示当前仓库中产品的数量。
对于生产者来说,其需要申请的资源为仓库中的剩余空间,因此,生产者在生产一个产品前,申请sem_produce信号量,当此信号量的值大于0,即有可用空间,将生产产品,并将sem_produce的值减去1(因为占用了一个空间);同时,当其生产一个产品后,当前仓库的产品数量增加1,需要将sem_custom信号量自动加1。
对于消费者来说,其需要申请的资源为仓库中的产品,因此,消费者在消费一个产品前,将申请sem_custom信号量,当此信号量的值大于0时,即有可用产品,将消费一个产品,并将sem_custom信号量的值减去(因为消费了一个产品),同时,当消费一个产品,当前仓库的剩余空间增加1,需要将sem_produce信号量自动加1。
生产者源代码:
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <errno.h> int sem_id; void init() { key_t key; int ret; unsigned short sem_array[2]; union semun { int val; struct semid_ds *buf; unsigned short *array; }arg; key=ftok(".",'s'); sem_id=semget(key,2,IPC_CREAT|0644); sem_array[0]=0; //identify the productor sem_array[1]=100; //identify the space //printf("set the productor init value is 0\nset the space init value is 100\n"); arg.array = sem_array; ret = semctl(sem_id, 0, SETALL, arg); if (ret == -1) printf("SETALL failed (%d)\n", errno); //printf("\nread the number\n"); printf("productor init is %d\n",semctl(sem_id,0,GETVAL)); printf("space init is %d\n\n",semctl(sem_id,1,GETVAL)); } void del() { semctl(sem_id,IPC_RMID,0); } int main(int argc,char *argv[]) { struct sembuf sops[2]; sops[0].sem_num = 0; sops[0].sem_op = 1; sops[0].sem_flg = 0; sops[1].sem_num = 1; sops[1].sem_op = -1; sops[1].sem_flg = 0; init(); printf("this is productor\n"); while(1) { printf("\n\nbefore produce:\n"); printf("productor number is %d\n",semctl(sem_id,0,GETVAL)); printf("space number is %d\n",semctl(sem_id,1,GETVAL)); semop(sem_id,(struct sembuf *)&sops[1],1); //get the space to instore the productor printf("now producing......\n"); semop(sem_id,(struct sembuf *)&sops[0],1); //now tell the customer can bu cusume printf("\nafter produce\n"); printf("spaces number is %d\n",semctl(sem_id,1,GETVAL)); printf("productor number is %d\n",semctl(sem_id,0,GETVAL)); sleep(4); } del(); }消费者源代码:
#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <errno.h> int sem_id; void init() { key_t key; key=ftok(".",'s'); sem_id=semget(key,2,IPC_CREAT|0644); //printf("sem id is %d\n",sem_id); } int main(int argc,char *argv[]) { init(); struct sembuf sops[2]; sops[0].sem_num = 0; sops[0].sem_op = -1; sops[0].sem_flg = 0; sops[1].sem_num = 1; sops[1].sem_op = 1; sops[1].sem_flg = 0; init(); printf("this is customer\n"); while(1) { printf("\n\nbefore consume:\n"); printf("productor is %d\n",semctl(sem_id,0,GETVAL)); printf("space is %d\n",semctl(sem_id,1,GETVAL)); semop(sem_id,(struct sembuf *)&sops[0],1); //get the productor to cusume printf("now consuming......\n"); semop(sem_id,(struct sembuf *)&sops[1],1); //now tell the productor can bu produce printf("\nafter consume\n"); printf("products number is %d\n",semctl(sem_id,0,GETVAL)); printf("space number is %d\n",semctl(sem_id,1,GETVAL)); sleep(3); } }要先运行生产者,在运行消费者。
生产者终端:
$ ./sem_productor productor init is 0 space init is 100 this is productor before produce: productor number is 0 space number is 100 now producing...... after produce spaces number is 99 productor number is 1 before produce: productor number is 1 space number is 99 now producing...... after produce spaces number is 98 productor number is 2消费者终端:
$ ./sem_customer this is customer before consume: productor is 2 space is 98 now consuming...... after consume products number is 1 space number is 99 before consume: productor is 2 space is 98 now consuming...... after consume products number is 1 space number is 99 before consume: productor is 1 space is 99 now consuming...... after consume products number is 0 space number is 100