1.什么是信號量
信號量是一種特殊的變量,訪問具有原子性。
只允許對它進行兩個操作:
1)等待信號量
當信號量值為0時,程序等待;當信號量值大於0時,信號量減1,程序繼續運行。
2)發送信號量
將信號量值加1。
我們使用信號量,來解決進程或線程間共享資源引發的同步問題。
2.Linux中信號量的使用
Linux提供了一組信號量API,聲明在頭文件sys/sem.h中。
1)semget函數:新建信號量
int semget(key_t key,int num_sems,int sem_flags);
key:信號量鍵值,可以理解為信號量的唯一性標記。
num_sems:信號量的數目,一般為1
sem_flags:有兩個值,IPC_CREATE和IPC_EXCL,
IPC_CREATE表示若信號量已存在,返回該信號量標識符。
IPC_EXCL表示若信號量已存在,返回錯誤。
返回值:相應的信號量標識符,失敗返回-1
2)semop函數:修改信號量的值
int semop(int sem_id,struct sembuf *sem_opa,size_t num_sem_ops);
sem_id:信號量標識符
sem_opa:結構如下
struct sembuf{ short sem_num;//除非使用一組信號量,否則它為0 short sem_op;//信號量在一次操作中需要改變的數據,通常是兩個數,一個是-1,即P(等待)操作, //一個是+1,即V(發送信號)操作。 short sem_flg;//通常為SEM_UNDO,使操作系統跟蹤信號, //並在進程沒有釋放該信號量而終止時,操作系統釋放信號量 };
3)semctl函數:用於信號量的初始化和刪除
int semctl(int sem_id,int sem_num,int command,[union semun sem_union]);
command:有兩個值SETVAL,IPC_RMID,分別表示初始化和刪除信號量。
sem_union:可選參數,結構如下:
union semun{ int val; struct semid_ds *buf; unsigned short *arry; };
一般用到的是val,表示要傳給信號量的初始值。
3.Linux信號量使用示例
下例中,我們寫了一個程序,程序中有一個char類型的字符,char message='x'
然后同時運行這個程序的兩個實例。
第一個實例,帶一個參數,將參數的第一個字符賦給message,比如為'0'
第二個實例,使用默認message值'x'
我們的目的是,使用信號量,循環執行這兩個實例,
我們可以看到執行結果應該是'x0x0x0x0x0x0'
#include<stdio.h> #include<stdlib.h> #include<sys/sem.h> union semun { int val; struct semid_ds *buf; unsigned short *array; }; int sem_id; int set_semvalue() { union semun sem_union; sem_union.val = 1; if(semctl(sem_id,0,SETVAL,sem_union)==-1) return 0; return 1; } int semaphore_p() { struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_op = -1; sem_b.sem_flg = SEM_UNDO; if(semop(sem_id,&sem_b,1)==-1) { fprintf(stderr,"semaphore_p failed\n"); return 0; } return 1; } int semaphore_v() { struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_op = 1; sem_b.sem_flg = SEM_UNDO; if(semop(sem_id,&sem_b,1)==-1) { fprintf(stderr,"semaphore_v failed\n"); return 0; } return 1; } void del_semvalue() { //刪除信號量 union semun sem_union; if(semctl(sem_id,0,IPC_RMID,sem_union)==-1) fprintf(stderr,"Failed to delete semaphore\n"); } int main(int argc,char *argv[]) { char message = 'x'; //創建信號量 sem_id = semget((key_t)1234,1,0666|IPC_CREAT); if(argc>1) { //初始化信號量 if(!set_semvalue()) { fprintf(stderr,"init failed\n"); exit(EXIT_FAILURE); } //參數的第一個字符賦給message message = argv[1][0]; } int i=0; for(i=0;i<5;i++) { //等待信號量 if(!semaphore_p()) exit(EXIT_FAILURE); printf("%c",message); fflush(stdout); sleep(1); //發送信號量 if(!semaphore_v()) exit(EXIT_FAILURE); sleep(1); } printf("\n%d-finished\n",getpid()); if(argc>1) { //退出前刪除信號量 del_semvalue(); } exit(EXIT_SUCCESS); }
輸出結果: