1. 程式人生 > >程序間通訊之systemV訊號量(semget semop semctl相關操作)

程序間通訊之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執行緒中)。