进程间通信之systemV信号量(semget semop semctl相关操作)

一、什么是信号量?

信号量的本质就是计数器,记录临界资源的数目,用来协助进程同步互斥的访问临界资源。为什么不在进程中定义一个全局变量作为计数器呢?两个进程间的地址空间是各自独立的,各自有各自的虚拟内存空间。多进程之间不能看到各自进程中的全局变量。(进程间的虚拟内存https://blog.csdn.net/jane_yao/article/details/81635979)既然多个进程都能对信号量进行操作,那信号量本身也是临界资源。

因为信号量本身也是临界资源,所以对信号量的增加减少操作是原子的PV操作(不可中断的)。

二、信号量的操作

systemV版本的信号量申请时是以信号量集为单位申请的 ,P操作为申请资源(资源数目减一)V操作释放资源(资源数目加一)申请不到资源就挂起等待。

1.创建: int semget(key_t key, int nsems, int semflg)

参数1key用ftok获取key_t ftok(const char *pathname, int proj_id)参数2所申请信号量集中的元素的个数,最少申请一个参数3为创建IPC_CREAT  | IPC_EXCL。返回值是创建的信号集标号。

2.初始化/删除:初始化和删除都是用的是 int semctl(int semid, int semnum, int cmd, ...);

参数1对标号semid的信号集操作,参数2信号集中下标为semnum的信号量进行设置,参数三创建时使用SETVAL删除时就直接使用IPC_RMID,初始化时有可变参数初始化就自己定义联合体

union semun {          

                    int              val;    /* Value for SETVAL */    

                   struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */          

                   unsigned short  *array;  /* Array for GETALL, SETALL */ 

                   struct seminfo  *__buf;  /* Buffer for IPC_INFO   (Linux-specific) */  

                     };SETVAL)

其中int val表示初始化时设置为几。

3.PV操作:int semop(int semid, struct sembuf *sops, unsigned nsops);

参数1对标号semid的信号量集进行操作,参数2信号量集中可能有多个信号量所以使用了系统自带的结构体sembuf

系统自带的sembuf的内容为

unsigned short sem_num; /* semaphore number */ //对哪个信号量进行操作 ,信号量在数组中的下标

short sem_op; /* semaphore operation */ //-1为P操作1为V操作

short sem_flg; /* operation flags */ //默认设置为0

参数3信号操作结构的数量,就是1

三、使用函数创建二元信号量实现互斥锁

创建两个进程,子进程打印C父进程打印P

  1 #include <stdio.h>                                                                                                   
  2 #include <unistd.h>                                                                                                  
  3 #include <sys/types.h>                                                                                               
  4 #include <sys/ipc.h>                                                                                                 
  5 #include <sys/sem.h>                                                                                                 
  6 #define PATH_NAME "/tmp"                                                                                             
  7 #define PROJ_ID 0x5555                                                                                               
  8                                                                                                                      
  9 union semun {                                                                                                        
 10     int val;    /* Value for SETVAL */                                                                               
 11     struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */                                                      
 12     unsigned short  *array;  /* Array for GETALL, SETALL */                                                          
 13     struct seminfo  *__buf;  /* Buffer for IPC_INFO                                                                  
 14     (Linux-specific) */                                                                                              
 15 };                                                                                                                   
 16 //初始化的flg为SETVAL,后为自己定义的联合体,联合体的值为要设置的值                                                    
 17 void initSem(int semid, int  num,int val)                                                                            
 18 {                                                                                                                    
 19   union semun arg; 
 20   arg.val = val;                                                                                                     
 21   semctl(semid,num,SETVAL,arg);                                                                                      
 22 }                                                                                                                    
 23                                                                                                                      
 24 static void PV(int semid,int num,int op)                                                                             
 25 {                                                                                                                    
 26   struct sembuf _sf;                                                                                                 
 27   _sf.sem_num = num;                                                                                                 
 28   _sf.sem_op = op;                                                                                                   
 29   _sf.sem_flg = 0;                                                                                                   
 30   semop(semid,&_sf,1);                                                                                               
 31 }                                                                                                                    
 32 void P(int semid,int num)                                                                                            
 33 {                                                                                                                    
 34   PV(semid,num, -1);                                                                                                 
 35 }                                                                                                                    
 36                            
 37 void V(int semid,int num)                                                                                            
 38 {                                                                                                                    
 39   PV(semid,num, 1);                                                                                                  
 40 }                                                                                                                    
 41                                                                                                                      
 42 int main()                                                                                                           
 43 {                                                                                                                    
 44   key_t k = ftok(PATH_NAME,PROJ_ID);                                                                                 
 45   if(k<0)                                                                                                            
 46   {                                                                                                                  
 47     printf("ftok error\n");                                                                                          
 48     return 1;                                                                                                        
 49   }                                                                                                                  
 50   int semid = semget(k,1,IPC_CREAT|IPC_EXCL|0666);                                                                   
 51   if(semid<0) 
 52   {                                                                                                                  
 53     return 2;                                                                                                        
 54   }                                                                                                                  
 55   sleep(5);                                                                                                          
 56   //初始化和删除都用到semctl,参数不同                                                                               
 57   //初始化时可变参数列表传结构体                                                                                     
 58   initSem(semid,0,1);//对信号集中下标为0的信号进行初始化,二元信号量所以最后一个参数为1                              
 59   //PV操作                                                                                                           
 60   pid_t id = fork();                                                                                                 
 61   if(id == 0)                                                                                                        
 62   {                                                                                                                  
 63     //child                                                                                                          
 64     while(1)                                                                                                         
 65     {                                                                                                                
 66       P(semid,0);                                                                                                    
 67       //打A时不能被干扰,实现互斥操作                                                                                
 68       printf("C");                                                                                                   
 69       usleep(12345);                                                                                                 
 70       fflush(stdout); 
 71       printf("C ");                                                                                                  
 72       usleep(32456);                                                                                                 
 73       fflush(stdout);                                                                                                
 74       V(semid,0);                                                                                                    
 75     }                                                                                                                
 76   }                                                                                                                  
 77   else                                                                                                               
 78   {                                                                                                                  
 79     while(1)                                                                                                         
 80     {                                                                                                                
 81       P(semid,0);                                                                                                    
 82       printf("P");                                                                                                   
 83       usleep(10323);                                                                                                 
 84       fflush(stdout);                                                                                                
 85       printf("P ");                                                                                                  
 86       usleep(34252);                                                                                                 
 87       fflush(stdout);                                                                                                
 88       V(semid,0); 
 89     }                                                                                                                
 90   }                                                                                                                  
 91   //删除                                                                                                             
 92  semctl(semid,0,IPC_RMID);                                                                                           
 93 }                                         

如果不加互斥锁则打印出的结果为:

可以看到打印P的进程可能会被打断,去打印C

加入互斥锁之后结果为:

就是想要的结果啦~

四、一些注意事项

和消息队列、共享内存一样生命周期随内核,进程结束不能自动释放必须使用ipcrm或者在程序末尾调用semctl.

POSIX信号量和systemV信号量作用相同,都是用于同步操作,但是POSIX可以用于线程间同步

为了解决进程间通信问题引入了临界资源,多个线程对临界资源的访问又会产生问题,为了保护临界资源引入了同步与互斥机制,为了实现同步与互斥引入信号量(进程中)、互斥锁(mutex_lock线程中)。

猜你喜欢

转载自blog.csdn.net/jane_yao/article/details/82216671
今日推荐