概念: IPC 信号量和内核信号量非常相似,是内核信号量的用户态版本。
优点:每个IPC信号量可以保护一个或者多个信号量值的集合,而不像内核信号量一样只有一个值,这意味着同一个IPC资源可以保护多个独立、共享的数据结构。另外,IPC信号量提供了一种失效安全机制,这是针对进程不能取消以前对信号量执行的操作就死亡的情况的。当进程使用这种机制时,由此引起的操作就是所谓的可取消的信号量操作。当进程死亡时,如果从来没有开始它的操作,那么它的所有IPC信号量都可以恢复成原来的值。这有助于防止其他使用相同信号量的进程无限地停留在阻塞状态,从而导致正在结束的进程不能手工取消它的信号量操作。
缺点:必须和受保护的资源搭配使用,常常和共享内存搭配使用。
基本原理:如果受保护的资源是可用的,那么信号量的值就是正数;如果受保护的资源现不能使用,那么信号量的值就是负数或0.要访问资源的进程试图把信号量的值减1,但是,内核阻塞这个进程,直到在这个信号量上的操作产生一个正值。当进程释放受保护的资源时,就把信号量的值增加1;在这样处理的过程中,其他所有正在等待这个信号量的进程都必须被唤醒。
代码示例:
#include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/sem.h> union semun { int val; struct semid_ds *buf; unsigned short *arry; }; static int sem_id = 0; static int set_semvalue(); static void del_semvalue(); static int semaphore_p(); static int semaphore_v(); int main(int argc, char *argv[]) { char message = 'X'; int i = 0; /* 创建信号量 */ sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT); if(argc > 1) { /* 程序第一次被调用,初始化信号量 */ if(!set_semvalue()) { fprintf(stderr, "Failed to initialize semaphore\n"); exit(EXIT_FAILURE); } /* 设置要输出到屏幕中的信息,即其参数的第一个字符 */ message = argv[1][0]; sleep(2); } for(i = 0; i < 10; ++i) { /* 进入临界区 */ if(!semaphore_p()) { exit(EXIT_FAILURE); } /* 向屏幕中输出数据 */ printf("%c", message); /* 清理缓冲区,然后休眠随机时间 */ fflush(stdout); sleep(rand() % 3); /* 离开临界区前再一次向屏幕输出数据 */ printf("%c", message); fflush(stdout); /* 离开临界区,休眠随机时间后继续循环 */ if(!semaphore_v()) { exit(EXIT_FAILURE); } sleep(rand() % 2); } sleep(10); printf("\n%d - finished\n", getpid()); if(argc > 1) { /* 如果程序是第一次被调用,则在退出前删除信号量 */ sleep(3); del_semvalue(); } exit(EXIT_SUCCESS); } static int set_semvalue() { /* 用于初始化信号量,在使用信号量前必须这样做 */ union semun sem_union; sem_union.val = 1; if(semctl(sem_id, 0, SETVAL, sem_union) == -1) { return 0; } return 1; } static void del_semvalue() { /* 删除信号量 */ union semun sem_union; if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1) { fprintf(stderr, "Failed to delete semaphore\n"); } } static int semaphore_p() { /* 对信号量做减1操作,即等待P(sv)*/ struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_op = -1;//P() sem_b.sem_flg = SEM_UNDO; if(semop(sem_id, &sem_b, 1) == -1) { fprintf(stderr, "semaphore_p failed\n"); return 0; } return 1; } static int semaphore_v() { /* 这是一个释放操作,它使信号量变为可用,即发送信号V(sv)*/ struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_op = 1;//V() sem_b.sem_flg = SEM_UNDO; if(semop(sem_id, &sem_b, 1) == -1) { fprintf(stderr, "semaphore_v failed\n"); return 0; } return 1; }
信号量集合的例子:
#include<stdio.h> #include<sys/types.h> #include<sys/ipc.h> #include<sys/sem.h> #include<errno.h> #include<string.h> #include<stdlib.h> #include<assert.h> #include<time.h> #include<unistd.h> #include<sys/wait.h> #define MAX_SEMAPHORE 10 #define FILE_NAME "test2.c" union semun{ int val ; struct semid_ds *buf ; unsigned short *array ; struct seminfo *_buf ; }arg; struct semid_ds sembuf; int main() { key_t key ; int semid ,ret,i; unsigned short buf[MAX_SEMAPHORE] ; struct sembuf sb[MAX_SEMAPHORE] ; pid_t pid ; pid = fork() ; if(pid < 0) { /* Create process Error! */ fprintf(stderr,"Create Process Error!:%s\n",strerror(errno)); exit(1) ; } if(pid > 0) { /* in parent process !*/ key = ftok(FILE_NAME,'a') ; if(key == -1) { /* in parent process*/ fprintf(stderr,"Error in ftok:%s!\n",strerror(errno)); exit(1) ; } semid = semget(key,MAX_SEMAPHORE,IPC_CREAT|0666); //创建信号量集合 if(semid == -1) { fprintf(stderr,"Error in semget:%s\n",strerror(errno)); exit(1) ; } printf("Semaphore have been initialed successfully in parent process,ID is :%d\n",semid); sleep(2) ; printf("parent wake up....\n"); /* 父进程在子进程得到semaphore的时候请求semaphore,此时父进程将阻塞直至子进程释放掉semaphore*/ /* 此时父进程的阻塞是因为semaphore 1 不能申请,因而导致的进程阻塞*/ for(i=0;i<MAX_SEMAPHORE;++i) { sb[i].sem_num = i ; sb[i].sem_op = -1 ; /*表示申请semaphore*/ sb[i].sem_flg = 0 ; } printf("parent is asking for resource...\n"); ret = semop(semid , sb ,10); //p() if(ret == 0) { printf("parent got the resource!\n"); } /* 父进程等待子进程退出 */ waitpid(pid,NULL,0); printf("parent exiting .. \n"); exit(0) ; } else { /* in child process! */ key = ftok(FILE_NAME,'a') ; if(key == -1) { /* in child process*/ fprintf(stderr,"Error in ftok:%s!\n",strerror(errno)); exit(1) ; } semid = semget(key,MAX_SEMAPHORE,IPC_CREAT|0666); if(semid == -1) { fprintf(stderr,"Error in semget:%s\n",strerror(errno)); exit(1) ; } printf("Semaphore have been initialed successfully in child process,ID is:%d\n",semid); for(i=0;i<MAX_SEMAPHORE;++i) { /* Initial semaphore */ buf[i] = i + 1; } arg.array = buf; ret = semctl(semid , 0, SETALL,arg); if(ret == -1) { fprintf(stderr,"Error in semctl in child:%s!\n",strerror(errno)); exit(1) ; } printf("In child , Semaphore Initailed!\n"); /* 子进程在初始化了semaphore之后,就申请获得semaphore*/ for(i=0;i<MAX_SEMAPHORE;++i) { sb[i].sem_num = i ; sb[i].sem_op = -1 ; sb[i].sem_flg = 0 ; } ret = semop(semid , sb , 10);//信号量0被阻塞 if( ret == -1 ) { fprintf(stderr,"子进程申请semaphore失败:%s\n",strerror(errno)); exit(1) ; } printf("child got semaphore,and start to sleep 3 seconds!\n"); sleep(3) ; printf("child wake up .\n"); for(i=0;i < MAX_SEMAPHORE;++i) { sb[i].sem_num = i ; sb[i].sem_op = +1 ; sb[i].sem_flg = 0 ; } printf("child start to release the resource...\n"); ret = semop(semid, sb ,10) ; if(ret == -1) { fprintf(stderr,"子进程释放semaphore失败:%s\n",strerror(errno)); exit(1) ; } ret = semctl(semid ,0 ,IPC_RMID); if(ret == -1) { fprintf(stderr,"semaphore删除失败:%s!\n",strerror(errno)); exit(1) ; } printf("child exiting successfully!\n"); exit(0) ; } return 0; }