Linux的信号量
信號量(semaphore)與已經介紹過的 IPC 結構不同,它是一個計數器。信號量用于實現進程間的互斥與同步,而不是用于存儲進程間通信數據。
**
1、特點
**
1、信號量用于進程間同步,若要在進程間傳遞數據需要結合共享內存。
2、信號量基于操作系統的 PV 操作,程序對信號量的操作都是原子操作。
3、每次對信號量的 PV 操作不僅限于對信號量值加 1 或減 1,而且可以加減任意正整數。
4、支持信號量組。
例子:就像一個房間,信號量相當于開門的鑰匙,房間相當于連接資源(一次僅允許一個進程使用的資源;如:A進程正在使用臨界資源,B進程就無法使用,等A進程完成后才行。)
在Linux中有許多的信號量集,
P操作:拿鎖;V操作:放回鎖;
**
2、信號量的相關函數
**
最簡單的信號量是只能取 0 和 1 的變量,這也是信號量最常見的一種形式,叫做二值信號量(Binary Semaphore)。而可以取多個正整數的信號量被稱為通用信號量。
Linux 下的信號量函數都是在通用的信號量數組上進行操作,而不是在一個單一的二值信號量上進行操作。
1 #include <sys/sem.h> 2 // 創建或獲取一個信號量組:若成功返回信號量集ID,失敗返回-1 3 int semget(key_t key, int num_sems, int sem_flags); 4 // 對信號量組進行操作,改變信號量的值:成功返回0,失敗返回-1 5 int semop(int semid, struct sembuf semoparray[], size_t numops); 6 // 控制信號量的相關信息 7 int semctl(int semid, int sem_num, int cmd, ...);semget創建新的信號量集合時,必須指定集合中信號量的個數(即num_sems),通常為1; 如果是引用一個現有的集合,則將num_sems指定為 0 。
所需頭文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
函數返回值:成功:返回信號量集的標識符;出錯:-1,錯誤原因存于error中
key:0(IPC_PRIVATE):會建立新信號量集對象,key大于0的32位整數:視參數semflg來確定操作,通常要求此值來源于ftok返回的IPC鍵值
nsems:創建信號量集中信號量的個數,該參數只在創建信號量集時有效
msgflg:
0:取信號量集標識符,若不存在則函數會報錯
IPC_CREAT:當semflg&IPC_CREAT為真時,如果內核中不存在鍵值與key相等的信號量集,則新建一個信號量集;如果存在這樣的信號量集,返回此信號量集的標識符
IPC_CREAT|IPC_EXCL:如果內核中不存在鍵值與key相等的信號量集,則新建一個消息隊列;如果存在這樣的信號量集則報錯
在semop函數中,sembuf結構的定義如下:
1 struct sembuf 2 { 3 short sem_num; // 信號量組中對應的序號,0~sem_nums-1 4 short sem_op; // 信號量值在一次操作中的改變量,取值為-1為P操作,取值為+1表示V操作 5 short sem_flg; // IPC_NOWAIT, SEM_UNDO 6 }sem_op 是一次操作中的信號量的改變量:
若sem_op > 0,表示進程釋放相應的資源數,將 sem_op 的值加到信號量的值上。如果有進程正在休眠等待此信號量,則換行它們。
若sem_op < 0,請求 sem_op 的絕對值的資源。
如果相應的資源數可以滿足請求,則將該信號量的值減去sem_op的絕對值,函數成功返回。
當相應的資源數不能滿足請求時,這個操作與sem_flg有關。
sem_flg 指定IPC_NOWAIT,則semop函數出錯返回EAGAIN。
sem_flg 沒有指定IPC_NOWAIT,則將該信號量的semncnt值加1,然后進程掛起直到下述情況發生:
1、當相應的資源數可以滿足請求,此信號量的semncnt值減1,該信號量的值減去sem_op的絕對值。成功返回;
2、此信號量被刪除,函數smeop出錯返回EIDRM;
3、進程捕捉到信號,并從信號處理函數返回,此情況下將此信號量的semncnt值減1,函數semop出錯返回EINTR
若sem_op == 0,進程阻塞直到信號量的相應值為0:
當信號量已經為0,函數立即返回。
如果信號量的值不為0,則依據sem_flg決定函數動作:
sem_flg指定IPC_NOWAIT,則出錯返回EAGAIN。
sem_flg沒有指定IPC_NOWAIT,則將該信號量的semncnt值加1,然后進程掛起直到下述情況發生:
1、信號量值為0,將信號量的semzcnt的值減1,函數semop成功返回;
2、此信號量被刪除,函數smeop出錯返回EIDRM;
3、進程捕捉到信號,并從信號處理函數返回,在此情況將此信號量的semncnt值減1,函數semop出錯返回EINTR
成功:返回信號量集的標識符;出錯:-1,錯誤原因存于error中。
在semctl函數中的命令有多種,這里就說兩個常用的:
1、SETVAL:用于初始化信號量為一個已知的值。所需要的值作為聯合semun的val成員來傳遞。在信號量第一次使用之前需要設置信號量。
2、IPC_RMID:刪除一個信號量集合。如果不刪除信號量,它將繼續在系統中存在,即使程序已經退出,它可能在你下次運行此程序時引發問題,而且信號量是一種有限的資源。
信號量(父子之間通信):
#include "stdio.h" #include <sys/sem.h> #include <sys/ipc.h> #include <sys/types.h> // int semget(key_t key, int num_sems, int sem_flags); // // 對信號量組進行操作,改變信號量的值:成功返回0,失敗返回-1 // int semop(int semid, struct sembuf semoparray[], size_t numops); // // 控制信號量的相關信息 // int semctl(int semid, int sem_num, int cmd, ...);union semun{int val;//**struct semid_ds *buf;unsigned short *array;struct seminfo *_buf;};void pGetKey(int id) {struct sembuf set;set.sem_num=0;set.sem_op=-1;set.sem_flg=SEM_UNDO;semop(id,&set,1);printf("getKey\n"); }void vPutBackKey(int id){struct sembuf set;set.sem_num=0;set.sem_op=1;set.sem_flg=SEM_UNDO;semop(id,&set,1);printf("put back the key\n");}int main() {key_t key;int semid;key =ftok(".",2);semid=semget(key,1,IPC_CREAT|0600);//1:表示信號量集合中有一個信號量;//獲取/創建信號量union semun initsem;initsem.val=0;semctl(semid,0,SETVAL,initsem);//0:操作弟0個信號量;初始化信號量;setval設置信號量的值,設置為initsemint pid=fork();if (pid>0){//去拿鎖pGetKey(semid);printf("this is father\n" );vPutBackKey(semid);//semctl(semid,0,IPC_RMID);}else if(pid==0){// printf("this is child\n");//pGetKey(semid);printf("this is child\n");vPutBackKey(semid);}else{printf("fork error\n");}}共享內存與信號量:
1 #include<stdio.h>2 #include<stdlib.h>3 #include<sys/sem.h>4 5 // 聯合體,用于semctl初始化6 union semun7 {8 int val; /*for SETVAL*/9 struct semid_ds *buf;10 unsigned short *array;11 };12 13 // 初始化信號量14 int init_sem(int sem_id, int value)15 {16 union semun tmp;17 tmp.val = value;18 if(semctl(sem_id, 0, SETVAL, tmp) == -1)19 {20 perror("Init Semaphore Error");21 return -1;22 }23 return 0;24 }25 26 // P操作:27 // 若信號量值為1,獲取資源并將信號量值-1 28 // 若信號量值為0,進程掛起等待29 int sem_p(int sem_id)30 {31 struct sembuf sbuf;32 sbuf.sem_num = 0; /*序號*/33 sbuf.sem_op = -1; /*P操作*/34 sbuf.sem_flg = SEM_UNDO;35 36 if(semop(sem_id, &sbuf, 1) == -1)37 {38 perror("P operation Error");39 return -1;40 }41 return 0;42 }43 44 // V操作:45 // 釋放資源并將信號量值+146 // 如果有進程正在掛起等待,則喚醒它們47 int sem_v(int sem_id)48 {49 struct sembuf sbuf;50 sbuf.sem_num = 0; /*序號*/51 sbuf.sem_op = 1; /*V操作*/52 sbuf.sem_flg = SEM_UNDO;53 54 if(semop(sem_id, &sbuf, 1) == -1)55 {56 perror("V operation Error");57 return -1;58 }59 return 0;60 }61 62 // 刪除信號量集63 int del_sem(int sem_id)64 {65 union semun tmp;66 if(semctl(sem_id, 0, IPC_RMID, tmp) == -1)67 {68 perror("Delete Semaphore Error");69 return -1;70 }71 return 0;72 }73 74 75 int main()76 {77 int sem_id; // 信號量集ID78 key_t key; 79 pid_t pid;80 81 // 獲取key值82 if((key = ftok(".", 'z')) < 0)83 {84 perror("ftok error");85 exit(1);86 }87 88 // 創建信號量集,其中只有一個信號量89 if((sem_id = semget(key, 1, IPC_CREAT|0666)) == -1)90 {91 perror("semget error");92 exit(1);93 }94 95 // 初始化:初值設為0資源被占用96 init_sem(sem_id, 0);97 98 if((pid = fork()) == -1)99 perror("Fork Error"); 100 else if(pid == 0) /*子進程*/ 101 { 102 sleep(2); 103 printf("Process child: pid=%d\n", getpid()); 104 sem_v(sem_id); /*釋放資源*/ 105 } 106 else /*父進程*/ 107 { 108 sem_p(sem_id); /*等待資源*/ 109 printf("Process father: pid=%d\n", getpid()); 110 sem_v(sem_id); /*釋放資源*/ 111 del_sem(sem_id); /*刪除信號量集*/ 112 } 113 return 0; 114 }總結
- 上一篇: 5种电脑定时关机的方法分享
- 下一篇: 好用的在线画图软件