Linux进程间通信(四)

时间:2022-07-11 19:06:01

一、什么是信号量:

  信号量的本质是一种数据操作锁,它本身不具有数据交换的功能,而是通过控制其他的通信资源(文件,外部设备)来实现进程间通信,它本身只是一种外部资源的标识。信号量在此过程中负责数据操作的互斥、同步等功能。

  当请求一个使用信号量来表示的资源时,进程需要先读取信号量的值来判断资源是否可用。大于0,资源可以请求,等于0,无资源可用,进程会进入睡眠状态直到资源可用。

  当进程不再使用一个信号量控制的共享资源时,信号量的值+1,对信号量的值进行的增减操作均为原子操作,这是由于信号量主要的作用是维护资源的互斥或多进程的同步访问。而在信号量的创建及初始化上,不能保证操作均为原子性。

二、为什么要使用信号量:

  为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任何时刻只能有一个执行线程访问代码的临界区域。临界区域是指执行数据更新的代码需要独占式地执行。而信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个线程在访问它,也就是说信号量是用来调协进程对共享资源的访问的。其*享内存的使用就要用到信号量。


三、信号量的工作原理

  由于信号量只能进行两种操作等待和发送信号,即P(sv)V(sv),他们的行为是这样的:
  P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行⾏
  V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而起,就给它加1
.

  举个例子,就是两个进程共享信号量sv,一旦其中一个进程执行了P(sv)操作,它将得到信号量,并可以进入临界区,使sv1。而第二个进程将被阻止进入临界区,因为当它试图执行P(sv)时,sv0,它会被挂起以等待第一个进程离开临界区域并执行V(sv)释放信号量,这时第二个进程就可以恢复执。

示例:

comm.h

  1 #pragma once
2
3 #include<stdio.h>
4 #include<sys/types.h>
5 #include<sys/ipc.h>
6 #include<sys/sem.h>
7
8 #define _PATH_NAME_ "/tmp"
9 #define _PROJ_ID_ 0x6666
10
11 union semun
12 {
13 int val;
14 struct semid_ds *buf;
15 unsigned short *array;
16 struct seminfo *_buf;
17 };
18
19 int creat_sem_set(int nums);
20 int get_sem_set();
21 int init_sem_set(int msg_id,int which,int val);
22 int destory_sem_set(int sem_id);
23 int P(int sem_id,int num);
24 int V(int sem_id,int num);

comm.c

  1 #include"comm.h"
2
3 static int comm_sem_set(int nums,int flags)
4 {
5 key_t _key=ftok(_PATH_NAME_,_PROJ_ID_);
6 if(_key<0)
7 {
8 perror("ftok");
9 return -1;
10 }
11 int sem_id=semget(_key,nums,flags);
12 if(sem_id<0)
13 {
14 perror("semget");
15 return -2;
16 }
17 return sem_id;
18 }
19 int creat_sem_set(int nums)
20 {
21 int flags=IPC_CREAT|IPC_EXCL|0666;
22 return comm_sem_set(nums,flags);
23 }
24 int get_sem_set()
25 {
26 int flags=IPC_CREAT;
27 return comm_sem_set(0,flags);
28 }
29 int destory_sem_set(int sem_id)
30 {
31 if(semctl(sem_id,0,IPC_RMID)<0)
32 {
33 perror("semctl");
34 }
35 return 0;
36 }
37 int init_sem_set(int msg_id,int which,int val)
38 {
39 union semun _un;
40 _un.val=val;
41 if(semctl(msg_id,which,SETVAL,_un)<0)
42 {
43 perror("semctl");
44 return -1;
45 }
46 return 0;
47 }
48 static int comm_op(int sem_id,int num,int op)
49 {
50 struct sembuf _sembuf;
51 _sembuf.sem_num=num;
52 _sembuf.sem_op=op;
53 _sembuf.sem_flg=0;
54 if(semop(sem_id,&_sembuf,1)<0)
55 {
56 perror("semop");
57 return -1;
58 }
59 return 0;
60 }
61 int P(int sem_id,int num)
62 {
63 int op=-1;
64 return comm_op(sem_id,num,op);
65 }
66 int V(int sem_id,int num)
67 {
68 int op=1;
69 return comm_op(sem_id,num,op);
70 }
test.c
  1 #include<unistd.h>  2 #include"comm.h"  3   4 int main()  5 {  6     int sem_id=creat_sem_set(1);  7   8     init_sem_set(sem_id,0,1);  9     pid_t id=fork(); 10     if(id==0) 11     {//child 12         int sem_id_child=get_sem_set(); 13         while(1) 14         { 15             P(sem_id_child,0); 16             printf("A"); 17             fflush(stdout); 18             usleep(rand()%300); 19             printf("A"); 20             fflush(stdout); 21             usleep(rand()%12345); 22             V(sem_id_child,0); 23         } 24     } 25     else 26     {//father 27         while(1) 28         { 29             P(sem_id,0); 30             printf("B"); 31             fflush(stdout); 32             usleep(rand()%300); 33             printf("B"); 34             fflush(stdout); 35             usleep(rand()%12345); 36             V(sem_id,0); 37         } 38         wait(NULL); 39         destroy_sem_set(sem_id); 40     } 41     return 0; 42 }
运行结果

Linux进程间通信(四)