- 信號量是一個計數器,一般在內核中實現,用於多個進程對共享數據對象的同步訪問。使用信號量的頭文件是#include <sys/sem.h>
- 信號量的使用規則:
-
若信號量爲正,則進程可以使用該資源。
- 若信號量爲0,則進程阻塞等待,並將進程插入等待隊列,直到該信號量的值大於0從等待隊列中執行進程請求。
- 加鎖操做:若是信號量大於0,則信號量-1;若是信號量爲0,則掛起該進程,並將這個進程插入等待隊列。
- 解鎖操做:若是等待隊列中有進程則喚醒該進程,讓它恢復運行;不然,信號量+1。
- Linux下使用信號量的經常使用函數:
- semget(key, num_sems, sem_flags):建立新的信號量或取得已有的信號量,key表示信號量的鍵值,不相關進程使用同一個key來訪問同一個信號量,num_sems表示信號量個數(通常爲1),sem_flags表示信號量訪問權限,用IPC_CREAT與權限位與可保證信號量不存在時新建一個。函數返回一個int類型的數值,表示信號量的標識符。
- semop(sem_id, sem_opa, num_sem_ops):改變信號量的值,改變操做在sem_opa中,sem_opa是sumbuf結構體對象,使用方法以下:
struct sembuf { short sem_num; //除非使用一組信號量,不然它爲0 short sem_op; //信號量在一次操做中須要改變的數據,一般是兩個數,一個是-1,即P操做(加鎖);一個是+1,即V操做(解鎖) short sem_flg; //一般爲SEM_UNDO,使操做系統跟蹤信號,並在進程沒有釋放該信號量而終止時,操做系統釋放信號量 };
- semctl(sem_id, sem_num, command, semun):控制信號量。commond中有:SETVAL初始化信號量爲一個值,該值再semun結構體的val字段;IPC_RMID用於刪除一個無需繼續使用的信號量。
- 信號量的使用實例,同時開兩個進程,每一個進程中都用信號量同步臨界區,在臨界區中向屏幕打印字符:
#include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/sem.h> union semun { int val; struct semid_ds *buf; unsigned short *arry; }; static int sem_id = 0; static int set_semvalue(); static void del_semvalue(); static int semaphore_p(); static int semaphore_v(); int main(int argc, char *argv[]) { char message = 'X'; int i = 0; //建立信號量 sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT); if(argc > 1) { //程序第一次被調用,初始化信號量 if(!set_semvalue()) { fprintf(stderr, "Failed to initialize semaphore\n"); exit(EXIT_FAILURE); } //設置要輸出到屏幕中的信息,即其參數的第一個字符 message = argv[1][0]; sleep(2); } for(i = 0; i < 10; ++i) { //進入臨界區 if(!semaphore_p()) exit(EXIT_FAILURE); //向屏幕中輸出數據 printf("%c", message); //清理緩衝區,而後休眠隨機時間 fflush(stdout); //離開臨界區,休眠隨機時間後繼續循環 if(!semaphore_v()) exit(EXIT_FAILURE); sleep(2); } sleep(3); printf("\n%d - finished\n", getpid()); if(argc > 1) { //若是程序是第一次被調用,則在退出前刪除信號量 sleep(3); del_semvalue(); } exit(EXIT_SUCCESS); } static int set_semvalue() { //用於初始化信號量,在sem_union的val字段中設置信號量初值。使用信號量以前必須先初始化! union semun sem_union; sem_union.val = 1; if(semctl(sem_id, 0, SETVAL, sem_union) == -1) return 0; return 1; } static void del_semvalue() { //刪除信號量 union semun sem_union; if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1) printf("Failed to delete semaphore\n"); } static int semaphore_p() { //對信號量作減1操做,即加鎖 P(sv) struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_op = -1; //P() sem_b.sem_flg = SEM_UNDO; if(semop(sem_id, &sem_b, 1) == -1) { printf("semaphore_p failed\n"); return 0; } return 1; } static int semaphore_v() { //這是一個釋放操做,它使信號量變爲可用,即解鎖 V(sv) struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_op = 1; //V() sem_b.sem_flg = SEM_UNDO; if(semop(sem_id, &sem_b, 1) == -1) { printf("semaphore_v failed\n"); return 0; } return 1; }
ps:以上代碼參考自
https://blog.csdn.net/ljianhui/article/details/10243617,運行結果以下:
