天天看點

程序間通信IPC-信号量

semget 

semget函數建立一個新的信号量或是獲得一個已存在的信号量鍵值。

1 int semget(key_t key, int num_sems, int sem_flags);      

第一個參數key是一個用來允許不相關的程序通路相同信号量的整數值。所有的信号量是為不同的程式通過提供一個key來間接通路的,對于每一個信号量系統 生成一個信号量辨別符。信号量鍵值隻可以由semget獲得,所有其他的信号量函數所用的信号量辨別符都是由semget所傳回的。還有一個特殊的信号量key值,IPC_PRIVATE(通常為0),其作用是建立一個隻有建立程序可以通路的信号量。這通常并沒有有用的目的,而幸運的是,因為在某些Linux系統上,手冊頁将IPC_PRIVATE并沒有阻止其他的程序通路信号量作為一個bug列出。num_sems參數是所需要的信号量數目。這個值通常總是1。sem_flags參數是一個标記集合,與open函數的标記十分類似。低九位是信号的權限,其作用與檔案權限類似。另外,這些标記可以與 IPC_CREAT進行或操作來建立新的信号量。設定IPC_CREAT标記并且指定一個已經存在的信号量鍵值并不是一個錯誤。如果不需 要,IPC_CREAT标記隻是被簡單的忽略。我們可以使用IPC_CREAT與IPC_EXCL的組合來保證我們可以獲得一個新的,唯一的信号量。如果這個信号量已經存在,則會傳回一個錯誤。如果成功,semget函數會傳回一個正數;這是用于其他信号量函數的辨別符。如果失敗,則會傳回-1。

semop 

函數semop用來改變信号量的值: 

第一個參數,sem_id,是由semget函數所傳回的信号量辨別符。第二個參數,sem_ops,是一個指向結構數組的指針,其中的每一個結構至少包含下列成員:

1 struct sembuf {
2     short sem_num;
3     short sem_op;
4     short sem_flg;
5 }      
第一個成員,sem_num,是信号量數目,通常為0,除非我們正在使用一個信号量數組。sem_op成員是信号量的變化量值。(我們可以以任何量改變信 号量值,而不隻是1)通常情況下中使用兩個值,-1是我們的P操作,用來等待一個信号量變得可用,而+1是我們的V操作,用來通知一個信号量可用。最後一個成員,sem_flg,通常設定為SEM_UNDO。這會使得作業系統跟蹤目前程序對信号量所做的改變,而且如果程序終止而沒有釋放這個信号量, 如果信号量為這個程序所占有,這個标記可以使得作業系統自動釋放這個信号量。将sem_flg設定為SEM_UNDO是一個好習慣,除非我們需要不同的行 為。如果我們确實變我們需要一個不同的值而不是SEM_UNDO,一緻性是十分重要的,否則我們就會變得十分迷惑,當我們的程序退出時,核心是否會嘗試清 理我們的信号量。semop的所用動作會同時作用,進而避免多個信号量的使用所引起的競争條件。我們可以在手冊頁中了解關于semop處理更為詳細的資訊。      

semctl 。semctl函數允許信号量資訊的直接控制:

1 int semctl(int sem_id, int sem_num, int command, ...);      

  

第一個參數,sem_id,是由semget所獲得的信号量辨別符。sem_num參數是信号量數目。當我們使用信号量數組時會用到這個參數。通常,如果 這是第一個且是唯一的一個信号量,這個值為0。command參數是要執行的動作,而如果提供了額外的參數,則是union semun,根據X/OPEN規範,這個參數至少包括下列參數:

1 union semun {
2     int val;
3     struct semid_ds *buf;
4     unsigned short *array;
5     struct seminfo* __buf;
6 }      

許多版本的Linux在頭檔案(通常為sem.h)中定義了semun聯合,盡管X/Open确認說我們必須定義我們自己的聯合。如果我們發現我們确實需 要定義我們自己的聯合,我們可以檢視semctl手冊頁了解定義。如果有這樣的情況,建議使用手冊頁中提供的定義,盡管這個定義與上面的有差別。有多個不同的command值可以用于semctl。在這裡我們描述兩個會經常用到的值。要了解semctl功能的詳細資訊,我們應該檢視手冊頁。這兩個通常的command值為:SETVAL:用于初始化信号量為一個已知的值。所需要的值作為聯合semun的val成員來傳遞。在信号量第一次使用之前需要設定信号量。IPC_RMID:當信号量不再需要時用于删除一個信号量辨別。semctl函數依據command參數會傳回不同的值。對于SETVAL與IPC_RMID,如果成功則會傳回0,否則會傳回-1。

使用信号量 

1 #include <sys/sem.h>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <unistd.h>
 5 #include <sys/wait.h>
 6 
 7 union semun
 8 {
 9      int val;
10      struct semid_ds* buf;
11      unsigned short int* array;
12      struct seminfo* __buf;
13 };
14 
15 void pv( int sem_id, int op )
16 {
17     struct sembuf sem_b;
18     sem_b.sem_num = 0;
19     sem_b.sem_op = op;
20     sem_b.sem_flg = SEM_UNDO;
21     semop( sem_id, &sem_b, 1 );
22 }
23 
24 int main( int argc, char* argv[] )
25 {
26     int sem_id = semget( IPC_PRIVATE, 1, 0666 );
27 
28     union semun sem_un;
29     sem_un.val = 1;
30     semctl( sem_id, 0, SETVAL, sem_un );
31 
32     pid_t id = fork();
33     if( id < 0 )
34     {
35         return 1;
36     }
37     else if( id == 0 )
38     {
39         printf( "child try to get binary sem\n" );
40         pv( sem_id, -1 );
41         printf( "child get the sem and would release it after 5 seconds\n" );
42         sleep( 5 );
43         pv( sem_id, 1 );
44         exit( 0 );
45     }
46     else
47     {
48         printf( "parent try to get binary sem\n" );
49         pv( sem_id, -1 );
50         printf( "parent get the sem and would release it after 5 seconds\n" );
51         sleep( 5 );
52         pv( sem_id, 1 );
53     }
54 
55     waitpid( id, NULL, 0 );
56     semctl( sem_id, 0, IPC_RMID, sem_un );
57     return 0;
58 }