【进程】进程通信-信号量(信号灯)
信号量
信号量(又名:信号灯)与其他进程间通信方式不大相同,主要用途是保护临界资源。
进程可以根据它判断是否能够访问某些共享资源。除了用于访问控制外,还可用于进程同步。
当进程A要获取临界资源S时,首先要获取临界资源的信号量M,M的初始值为1,当获取到M并发现M的值大于1时,可以反问临界资源M,且信号量-1,M=0。
当进程B也要访问临界资源S时,也要首先获取临界资源S的信号量M,发现M的值为0时,无法获取临界资源S,进程B阻塞等待。
当进程A访问完临界资源后,信号量M加1,变为M=1,同时唤醒进程B,使得B能够访问临界资源S,以此类推,交替访问临界资源S。
分类
二值信号灯:信号灯的值只能取0或者1,类似于互斥锁。但两者有不同:信号灯强调共享资源,只要共享资源可用,其他进程同样可以修改信号灯的值;互斥锁更强调进程,占用资源的进程使用完资源后,必须由进程本身来解锁。
计数信号灯:信号灯的值可以取任意非负值。
创建/打开
#include<sys/types.h> #include<sys/ipc.h> #include<sys/sem.h> int semget(key_t key,int nsems,int semflg)
- key:键值,由ftok获得。
- nsems:指定打开或者新创建的信号灯中将包含信号灯的数目。
- semflg:标识,同消息队列,访问权限。
信号量的操作
int semop(int semid,struct sembuf *sops,unsigned nsops)
- 功能:对信号量进行控制。
- semid:信号量集的ID
- sops:是一个操作数组,表明要进行什么操作。
- nsops:sops所指向的数组元素个数。
struct sembuf { unsigned short sem_num; /*表示你要获取信号量在信号集中的索引*/ short sem_op; /*决定获取还是释放信号量*/ short sem_flg; }
sem_op:决定获取还是释放信号量
- 0:等待,直到信号灯的值变成0
- 1:释放资源,V操作
- -1:分配资源,P操作
Sem_flg:信号操作标志,可能选择有两种:
- IPC_NOWAIT:对信号的操作不能满足时,semop不会阻塞,并立即返回,同时设定错误信息。
- IPC_UNDO:程序结束时(无论正常还是非正常结束)释放信号量,这样做的目的在于避免程序在异常情况下结束时未将锁定的资源解锁,造成该资源永远锁定。
信号量实例
1 /********************************************************** 2 *程序要求: 创建两个进程,通过信号灯控制进程中一段代码的执行循序。 3 *功能描述: 本程序通过使用信号灯,实现了一种基于父子进程的简单的进程同步。 4 **********************************************************/ 5 #include<stdio.h> 6 #include<sys/types.h> 7 #include<sys/ipc.h> 8 #include<sys/sem.h> 9 #include<stdlib.h> 10 #include<math.h> 11 #include<errno.h> 12 13 /* 14 * 函数入口 15 * */ 16 int main() 17 { 18 int pid,semid; 19 key_t semkey; 20 21 if((semkey=ftok("./samaphore.c",1))<0)//将文件路径和项目ID(后面那个“1“)转换为System V IPC Key 22 { 23 printf("ftok failed\n"); 24 exit(EXIT_FAILURE); 25 } 26 printf("ftok ok, semkey=%d\n",semkey); 27 if((semid=semget(semkey,1,IPC_CREAT|IPC_EXCL|0700))<0)//创建信号灯集,其中包含1个信号灯 28 { 29 printf("semget failed\n"); 30 exit(EXIT_FAILURE); 31 } 32 printf("semget ok, semid=%d\n",semid); 33 34 if((semctl(semid,0,SETVAL,1))<0)//设置semid对应的信号集中第一个信号灯的semval为1。 35 { 36 printf("semctl set semval failed\n"); 37 exit(EXIT_FAILURE); 38 } 39 printf("semctl set semval ok\n"); 40 if((pid=fork())<0) 41 { 42 printf("fork failed\n"); 43 exit(EXIT_FAILURE); 44 } 45 else if(pid>0)//父进程,先索取共享资源,而后释放 46 { 47 struct sembuf p_op_buf,v_op_buf; 48 49 p_op_buf.sem_num=0; 50 p_op_buf.sem_op=-1; //-1为分配资源,0为等待,1为释放资源 51 if(semop(semid,&p_op_buf,1)<0)//以上三行向semid代表的信号灯集的第一个信号灯申请一个资源,即使semval减1 52 { 53 printf("semop failed\n"); 54 exit(EXIT_FAILURE); 55 } 56 printf("father get the semaphore\n"); 57 sleep(3); 58 printf("father release the semaphore\n"); 59 v_op_buf.sem_num=0; 60 v_op_buf.sem_op=1; //释放资源 61 v_op_buf.sem_flg=0; 62 if(semop(semid,&v_op_buf,1)<0)//以上三行将释放一个资源给semid代表的信号灯集第一个信号灯 63 { 64 printf("semop release semaphore failed\n"); 65 exit(EXIT_FAILURE); 66 } 67 } 68 else//子进程不断申请共享资源,申请到后声明一下,然后释放 69 { 70 struct sembuf p_op_buf,v_op_buf; 71 sleep(1);//等待父进程将唯一的资源占用 72 printf("child wait for the semaphore\n"); 73 p_op_buf.sem_num=0; 74 p_op_buf.sem_op=-1; 75 p_op_buf.sem_flg=0;//该标志位不用要清零,此处不清零将出现错误 76 if(semop(semid,&p_op_buf,1)<0)//向semid代表的信号灯集的第一个信号灯申请一个资源,申请不到就会阻塞,直到有了资源 77 { 78 printf("semop get semaphore failed\n"); 79 exit(EXIT_FAILURE); 80 } 81 printf("child get the semaphore\n"); 82 if(semctl(semid,0,IPC_RMID,0)<0) 83 { 84 printf("semctl remove semaphore set failed\n"); 85 exit(EXIT_FAILURE); 86 } 87 } 88 }