信號量

一、信號量分類

(1)Posix有名信號量,可用於進程和同一進程不同線程間同步

(2)​Posix基於內存的信號量,存放在共享內存區中,可用於進程和同一進程不同線程間同步

(3)System V信號量​,在內核中維護,可用於進程和同一進程不同線程間同步。

二、信號量、互斥鎖、條件變量的差異

(1)互斥鎖必須總是由給他上鎖的線程解鎖,信號量的掛出(post)卻不必由執行過他等待操作(wait)的線程執行。​

(2)互斥鎖要麼被鎖住要麼被解鎖。

(3)既然信號量有一個與之關聯的狀態(計數值)那麼信號量的掛出操作總是被記住。然而當向一個條件變量發送信號時,如果沒有線程等待在該條件變量上,那麼該條件變量將丟失。


​三、有名信號量

(1)由sem_open來創建一個新的信號量或打開一個已存在的信號量。其格式爲:

sem_t *sem_open(const char *name,int oflag,mode_t mode,unsigned int value); 

返回:若成功則爲指向信號量的指針,若出錯則爲SEM_FAILED 

其中,第三、四個參數可以沒有,主要看第二個參數如何選取。

oflag參數:可以是0、O_CREAT或O_CREAT|O_EXCL。如果指定O_CREAT標誌而沒有指定O_EXCL,那麼只有當所需的信號量尚未存在時才初始化它。但是如果所需的信號量已經存在也不會出錯。 但是如果在所需的信號量存在的情況下指定O_CREAT|O_EXCL卻會報錯。mode參數:指定權限位。value參數:指定信號量的初始值。該初始值不能超過SEM_VALUE_MAX(這個常值必須至少爲32767).二值信號量的初始值通常爲1,計數信號量的初始值則往往大於1。用sem_close來關閉該信號量。

(2)使用sem_close來關閉信號量

int sem_close(sem_t* sem);​

一個進程終止時系統對其上面已經打開的所有有名信號量執行這樣的操作。​

​(3)使用sem_unlink刪除信號量:

int sem_unlink(const char *name);

 返回:成功返回0,出錯返回-1

每個信號量都有一個引用計數器記錄當前信號量打開的次數,當計數大於0時sem就能從系統中刪除​,只是其析構要等到最後一個被sem_close之後才發生。

(4)獲取信號量的當前值:

int sem_getvalue(sem_t *sem,int *valp); 

返回:成功返回0,出錯返回-1

​sem_getvalue在由valp指向的整數中返回所指定信號量的當前值。如果信號量當前已上鎖,那麼返回值或爲0,或爲某個負數,絕對值即爲等待等待該信號量解鎖的線程數。​

(5)信號量的等待:(P操作,也稱爲遞減down 或 上鎖lock)

int sem_wait(sem_t *sem);

​int sem_trywait(sem_t *sem); 

​返回:成功返回0,出錯返回-1

​sem_wait函數測試所指定信號量的值,如果該值大於0,就將它的值減1並立即返回;如果該值等於0,調用線程就被投入睡眠中,直到該值變爲大於0,這時再將它減1,函數隨後返回。“測試並減1”操作必須是原子的。sem_wait和sem_trywait的差別是:當所指定信號量的值已經是0時,後者並不將調用的進程投入睡眠。相反,它返回一個EAGAIN錯誤。如果被某個信號中斷,sem_wait就可能過早的返回,返回的錯誤爲EINTR。

(6)信號量掛出(V操作,也稱爲遞增up 或解鎖unlock)

int sem_post(sem_t *sem);

返回:成功返回0,出錯返回-1 將所指定的信號量值加1

​互斥鎖是爲上鎖而優化的,條件變量是爲等待而優化的,信號量即可用於上鎖也可用於等待,所以可能導致更多的開銷和更高的複雜性。

四、Posix基於內存的信號量

Posix有名信號量創建時候是用一個name參數標識,它通常指代文件系統中的某個文件。而基於內存的信號量是由應用程序分配信號量的內存空間,即分配一個sem_t數據類型的內存空間,然後由系統初始化它們的值。操作函數如下:

#include

​int sem_init(sem_t *sem, int pshared, unsigned int value);   //初始化內存信號量int sem_destroy(sem_t *sem);   //摧毀信號量

如果shared=0,那麼待初始化的信號量是在同一進程的各個線程間共享的,否則該信號量是在進程間共享的,此時該信號量必須存放在某種類型的共享內存區中,使得用它的進程能夠訪問該共享內存區。value是該信號量的初始值。

​五、信號量的限制

SEM_NSEMS_MAX:一個進程可以同時打開着的最大信號量個數

SEM_VALUE_MAX:一個信號量的最大值。

​六、System V信號量​

System V 信號量在內核中維護,其中包括二值信號量 、計數信號量、計數信號量集。

二值信號量 : 其值只有0、1 兩種選擇,0表示資源被鎖,1表示資源可用;

計數信號量:其值在0 和某個限定值之間,不限定資源數只在0 1 之間;

計數信號量集 :多個信號量的集合組成信號量集內核維護的信號量集結構信息如下:定義在頭文件

struct semid_ds {    

struct     ipc_perm     sem_perm;    

​struct     sem          *sem_base;    

   ​ushort                  sem_nsems;
    time_t                  sem_otime;
    time_t                  sem_ctime;
};

其中ipc_perm 結構是內核給每個進程間通信對象維護的一個信息結構,其成員包含所有者用戶id,所有者組id、創建者及其組id,以及訪問模式等;semid_ds結構體中的sem結構是內核用於維護某個給定信號量的一組值的內部結構,其結構定義:

struct sem {   int semval;     
   int sempid;     
   struct list_head sem_pending; 
 };

其中senval變量代表當前信號量的值,sempid 爲最後一個成功操作該信號量的進程id,該結構體在內核以雙向鏈表進行 維護semid_ds結構體中的sem_nsems成員代表該信號量標示符的信號量個數主要函數介紹:

(1)創建一個信號量或訪問一個已經存在的信號量集。

int semget(key_t key, int nsems, int semflg);

該函數執行成功返回信號量標示符,失敗返回-1參數key是通過調用ftok函數得到的鍵值,nsems代表創建信號量的個數,如果只是訪問而不創建則可以指定該參數爲0,我們一旦創建了該信號量,就不能更改其信號量個數,只要你不刪除該信號量,你就是重新調用該函數創建該鍵值的信號量,該函數只是返回以前創建的值,不會重新創建;semflg 指定該信號量的讀寫權限,當創建信號量時不許加IPC_CREAT ,若指定IPC_CREAT |IPC_EXCL則創建是存在該信號量,創建失敗。​

(2)打開一個信號量集後,對其中一個或多個信號量的操作。

int semop(int semid, struct sembuf *sops, unsigned nsops);

該函數執行成功返回0,失敗返回-1;第一個參數semid 爲信號量標示符;nops爲第二個參數的操作數組的個數,第二個參數sops爲一個結構體數組指針,結構體定義在sys/sem.h中,結構體如下​

struct sembuf {
   unsigned short sem_num; 
   short sem_op; 
   short sem_flg; };

sem_num 操作信號的下標,其值可以爲0 到nopssem_flg爲該信號操作的標誌:其值可以爲0、IPC_NOWAIT 、 SEM_UNDO0 在對信號量的操作不能執行的情況下,該操作阻塞到可以執行爲止;IPC_NOWAIT 在對信號量的操作不能執行的情況下,該操作立即返回;SEM_UNDO當操作的進程推出後,該進程對sem進行的操作將被取消;sem_op取值 >0 則信號量加上它的值,等價於進程釋放信號量控制的資源sem_op取值 =0若沒有設置IPC_NOWAIT, 那麼調用進程將進入睡眠狀態,直到信號量的值爲0,否則進程直接返回sem_op取值 <0則信號量加上它的值,等價於進程申請信號量控制的資源,若進程設置IPC_NOWAIT則進程再沒有可用資源情況下,進程阻塞,否則直接返回。

​(3)對信號量執行各種控制操作。

int semctl(int semid, int semnum, int cmd, ...);

該函數執行成功返回非負值,失敗返回-1參數semid爲信號集的標識符,參數 semnum標識一個特定信號,該參數僅用於 SETVAL、GETVAL、GETPID命令cmd控制類型,...說明函數參數是可選的,通過該共用體變量semun選擇操作參數,各字段如下:​

union semun {    int val; 
    struct semid_ds __user *buf; 
    unsigned short __user *array; 
    struct seminfo __user *__buf; 
    void __user *__pad;
 };

IPC_STAT讀取一個信號量集的數據結構semid_ds,並將其存儲在semun中的buf參數中。IPC_SET設置信號量集的數據結構semid_ds中的元素ipc_perm,其值取自semun中的buf參數。IPC_RMID將信號量集從系統中刪除GETALL用於讀取信號量集中的所有信號量的值,存於semnu的array中SETALL 設置所指定的信號量集的每個成員semval的值GETPID返回最後一個執行semop操作的進程的PID。LSETVAL把的val數據成員設置爲當前資源數GETVAL把semval中的當前值作爲函數的返回,即現有的資源數,返回值爲非負數。

發佈了43 篇原創文章 · 獲贊 6 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章