一、信号灯简介Linux支持System V的信号灯(semaphore),是一种进程间通信的方式,只不过它和管道、FIFO或者共享内存不一样,信号灯主要用于同步或者互斥对共享资源的访问,它的发明来源于火车运行系统中的"信号灯",利用信号灯可以实现"PV"操作这种进程间同步进制。P操作时获得资源,将信号灯的值减1,如果结果不为负则执行完毕,进程获得资源,否则进程睡眠以等待的进程释放;V操作则是释放资源,给信号灯的值加1, 唤醒一个因执行P操作而等待的进程。二、信号灯的两种类型A、二值信号灯最简单的信号灯形式,信号灯的值只能取0或1,类似互斥锁。注意:虽然二值信号灯能够实现互斥锁的功能,但两者的关注内容不同。信号灯强调共享资源,只要共享资源可用,其他进程同样可以修改信号灯的值;互斥锁更强调进程,占用资源的进程使用完资源后,必须由进程本身来解锁。B.计数信号灯信号灯的值可以取任意非负值(当然受内核本省的约束),用来统计资源,其值就代表可用资源的个数。注意:通常所说的系统V信号灯指的是计数信号灯集三、相关APIA.创建一个信号灯集功能:创建一个信号灯集并返回这个信号灯集的ID 或直接返回一个已经存在的信号灯集的ID参数说明:key:如果key值为IPC_PRIVATE或key为0并且semflg设置了IPC_CREAT,此时调用此函数总是创建一个新的信号灯集(我们在共享内存的时候验证过,还记的吗?其实system v 的ipc对象的创建机制都很类似。nsems:指定这个信号灯集中信号灯的个数(每个信号灯代表了某一类资源)。semflg:可以指定为IPC_CREAT | 0666,其含义为,不存在则创建,访问权限为0666。我们也可以通过IPC _CREAT | IPC_EXCL一起使用的时候确定要创建的信号灯集是否存在,如果存在此时这个函数放回-1 。B.控制信号灯集参数说明:semid : 信号灯集IDsemnum:要修改的信号灯编号(创建信号灯集时,信号灯的编号从0开始)cmd:IPC_STAT 获取信号灯信息,信息由arg.buf(即第四个参数)返回GETVAL : 获取semnum信号灯的值SETVAL : 设置semnum信号灯的值IPC_RMID : 从系统中删除semnum所代表的信号灯 注意:semctl是可变参数,cmd为GETVAL或SETVAL时,需要传递第四个参数,其参数类型为 union semun。这个结构体的类型必须在应用程序中定义,定义如下:设置信号灯集中第一个信号灯的数值为1(即某一类资源的个数)a.必须在应用程序中定义如下类型union semun{ int val; struct semid_ds *buf; unsigned short *array; struct seminfo * __buf;};b.定义一个union semun变量,并赋值union semun mysemun;mysemun.val = 1;c.调用semctl函数//第一个信号灯的编号为0if ( ( semctl ( semid , 0 , SETVAL,mysemun)) { perror("Fail to semctl"); exit(EXIT_FAILURE);}删除一个信号灯if( semctl ( semid , 0 , IPC_RMID , 0 ) { perror("Fail to semctl IPC_RMID"); exit(EXIT_FAILURE);}C.操作信号灯功能:semop系统调用可以实现对由semid标志的信号等集中的某一个指定信号灯的一系列操作参数说明:semid信号灯集的标识ID。sops :指向结构体sembuf的指针,设置信号灯集中某一个信号灯的工作方式。sem_num对应信号灯集中的信号灯,0代表第一个信号灯sem_op值决定了对sem_num的三种不同操作:a.sem_op = 0,调用者阻塞等待,直到信号灯的值等于0时返回。可以用来测试共享资源是否已用完。b.sem_op = 1,释放资源,V操作c.sem_op = -1,分配资源,P操作sem_flg可取0,IPC_NOWAIT以及SEM_UNDO三个标志a. 0 代表阻塞调用(资源不满足阻塞)b. IPC_NOWAIT 代表非阻塞调用c. 如果设置了SEM_UNDO标志,那么在进程结束时,相应的操作将被取消,这是一个比较重要的一个标志。案例:封装一个P操作和一个V操作//p操作int my_sem_wait(int semid,int sem_num){ struct sembuf op; op.sem_num = sem_num; op.sem_op = -1; op.sem_flg = 0; if(semop(sem_id,&op,1) {perror("fail to semop");exit(-1); } return 0;}//V操作int my_sem_wait(int semid,int sem_num){ struct sembuf op; op.sem_num = sem_num; op.sem_op = 1; op.sem_flg = 0; if(semop(sem_id,&op,1) {perror("fail to semop");exit(-1); } return 0;}案例探究(用信号灯集实现共享内存间的同步):A .读共享内存#include stdio.h>#include stdlib.h>#include string.h>#include errno.h>#include unistd.h>#include sys/ipc.h>#include sys/shm.h>#include sys/types.h>#include sys/sem.h>#define READ 0#define WRITE 1#define N 2union semun{ int val; struct semid_ds *buf; unsigned short *array; struct seminfo *__buf;};//初始化信号灯集中的信号灯的值int my_sem_init(int semid){ int i = 0; union semun mysemun; for(i = 0;i N;i ++) { mysemun.val = i; if(semctl(semid,i,SETVAL,mysemun) 0) { perror("Fail to semctl"); return -1; } } return 0;}//信号灯集中的信号灯释放资源int my_sem_post(int semid,int sem_num){ struct sembuf op; op.sem_num = sem_num; op.sem_op = 1; op.sem_flg = 0; if(semop(semid,&op,1) 0) { perror("Fail to semop"); return -1; } return 0;}//信号灯集中的信号灯申请资源 int my_sem_wait(int semid,int sem_num){ struct sembuf op; op.sem_num = sem_num; op.sem_op = -1; op.sem_flg = 0; if(semop(semid,&op,1) 0) { perror("Fail to semop"); return -1; } return 0;}//读共享内存int read_share_memory(int semid,char *addr){ int n; while(1) { my_sem_wait(semid,READ); printf("Read : %s.\n",addr); my_sem_post(semid,WRITE); if(strncmp(addr,"quit",4) == 0) { if(shmdt((void *)addr) 0) { perror("Fail to semdt"); return -1; } break; } } return 0;}int main(int argc,char *argv[]){ key_t key; void *shmaddr; int shmid,semid; if(argc 2) { fprintf(stderr,"usage : %s argv[1].\n",argv[0]); exit(EXIT_FAILURE); } //获取键值 if((key = ftok(argv[1],'a')) 0) { perror("Fail to ftok"); exit(EXIT_FAILURE); } //创建共享内存 if((shmid = shmget(key,1024,IPC_CREAT | 0666)) 0) { perror("Fail to shmget"); exit(EXIT_FAILURE); } //映射共享内存到进程地址空间 if((shmaddr = shmat(shmid,NULL,0)) == (void *)-1) { perror("Fail to shmat"); exit(EXIT_FAILURE); } //创建含有2个的信号灯的信号灯集 if((semid = semget(key,N,IPC_CREAT | 0666)) 0) { perror("Fail to shmget"); exit(EXIT_FAILURE); } //初始化信号灯集中的信号灯 my_sem_init(semid); read_share_memory(semid,(char *)shmaddr); exit(EXIT_SUCCESS);}写共享内存#include stdio.h>#include stdlib.h>#include string.h>#include errno.h>#include unistd.h>#include sys/ipc.h>#include sys/shm.h>#include sys/types.h>#include sys/sem.h>#define READ 0#define WRITE 1#define N 2union semun{ int val; struct semid_ds *buf; unsigned short *array; struct seminfo *__buf;}mysemun;//初始化信号灯集中的信号灯的值int my_sem_init(int semid){ int i = 0; for(i = 0;i N;i ++) { mysemun.val = i; if(semctl(semid,i,SETVAL,mysemun) 0) { perror("Fail to semctl"); return -1; } } return 0;}//信号灯集中的信号灯释放资源int my_sem_post(int semid,int sem_num){ struct sembuf op; op.sem_num = sem_num; op.sem_op = 1; op.sem_flg = 0; if(semop(semid,&op,1) 0) { perror("Fail to semop"); return -1; } return 0;}//信号灯集中的信号灯申请资源 int my_sem_wait(int semid,int sem_num){ struct sembuf op; op.sem_num = sem_num; op.sem_op = -1; op.sem_flg = 0; if(semop(semid,&op,1) 0) { perror("Fail to semop"); return -1; } return 0;}//读共享内存int read_share_memory(int semid,char *addr){ int n; while(1) { my_sem_wait(semid,WRITE); printf(">"); fgets(addr,1024,stdin); addr[strlen(addr)-1] = '\0'; my_sem_post(semid,READ); if(strncmp(addr,"quit",4) == 0) { if(shmdt((void *)addr) 0) { perror("Fail to semdt"); return -1; } break; } } return 0;}int main(int argc,char *argv[]){ key_t key; void *shmaddr; int shmid,semid; if(argc 2) { fprintf(stderr,"usage : %s argv[1].\n",argv[0]); exit(EXIT_FAILURE); } //获取键值 if((key = ftok(argv[1],'a')) 0) { perror("Fail to ftok"); exit(EXIT_FAILURE); } //创建共享内存 if((shmid = shmget(key,1024,IPC_CREAT | 0666)) 0) { perror("Fail to shmget"); exit(EXIT_FAILURE); } //映射共享内存到进程地址空间 if((shmaddr = shmat(shmid,NULL,0)) == (void *)-1) { perror("Fail to shmat"); exit(EXIT_FAILURE); } //创建含有2个的信号灯的信号灯集 if((semid = semget(key,N,IPC_CREAT | 0666)) 0) { perror("Fail to shmget"); exit(EXIT_FAILURE); } //初始化信号灯集中的信号灯 my_sem_init(semid); read_share_memory(semid,(char *)shmaddr); if(shmctl(shmid,IPC_RMID,NULL) 0) { perror("Fail to shmctl"); return -1; } if(semctl(semid,0,IPC_RMID,0) 0) { perror("Fail to semctl WRITE"); return -1; } exit(EXIT_SUCCESS);} 02-11 11:40