对linux信号量的理解以及实现
通信資源(文件,外部設備)來實現進程間通信,它本身只是一種外部資源的標識。信號
量在此過程中負責數據操作的互斥、同步等功能。
? ? 當請求一個使用信號量來表示的資源時,進程需要先讀取信號量的值來判斷資源是否可
用。大于0,資源可以請求,等于0,無資源可用,進程會進入睡眠狀態直至資源可用。
? 當進程不再使用一個信號量控制的共享資源時,信號量的值+1,對信號量的值進行的增減
操作均為原子操作,這是由于信號量主要的作用是維護資源的互斥或多進程的同步訪問。
而在信號量的創建及初始化上,不能保證操作均為原子性。
一. 為什么要使用信號量?
為了防止出現因多個程序同時訪問一個共享資源而引發的一系列問題,我們需要一種方法,
它可以通過生成并使用令牌來授權,在任何時刻只能有一個執行線程訪問代碼的臨界區域。
臨界區域是指執行數據更新的代碼需要獨占式地執行。而信號量就可以提供這樣的一種訪
問機制,讓一個臨界區同一時間只有一個線程在訪問它, 也就是說信號量是用來調協進程
對共享資源的訪問的。其中共享內存的使用就要用到信號量。
二. 信號量的工作原理
由于信號量只能進行兩種操作等待和發送信號,即P(sv)和V(sv),他們的行為是這樣的:
P(sv):如果sv的值等于零,就給它減1;如果它的值為零,就掛起該進程的執行
V(sv):如果有其他進程因等待sv而被掛起,就讓它恢復運行,如果沒有進程因等待sv而掛
起,就給它加1.?
??
舉個例子,就是兩個進程共享信號量sv,一旦其中一個進程執行了P(sv)操作,它將得到信號
量,并可以進入臨界區,使sv減1。而第二個進程將阻止進入臨界區,因為當它試圖執行
P(sv)時,sv為0,它會被掛起以等待第一個進程離開臨界區域并執行V(sv)釋放信號量,這時
第二個進程就可以恢復執行。
三 .Linux的信號量機制
Linux提供了一組精心設計的信號量接口來對信號量進行操作,它們不只是針對二進制信號
量,下面將會對這些函數進行介紹,但請注意,這些函數都是用來對成組的信號量值進行
操作的。它們聲明在頭文件sys/sem.h中。
信號量的意圖在于進程間同步,互斥鎖和條件變量的意圖則在于線程間同步。但是信號
量也可用于線程間,互斥鎖和條件變量也可用于進程間。我們應該使用適合具體應用的那
組原語。
簡易的信號量實現:
comm.h
#ifndef _COMM_H_ #define _COMM_H_ #include<sys/sem.h> #include<sys/types.h> #include<sys/ipc.h> #include<unistd.h> #include<stdlib.h> #include<stdio.h> #define PATHNAME "." #define PROJ_ID "0x6666"typedef struct mysembuf {unsigned short int sem_num; /* semaphore number */short int sem_op; /* semaphore operation */short int sem_flg; /* operation flag */ }mysembuf;typedef union semun {int val;struct semid_ds *buf;unsigned short *array;struct seminfo*_buf; }_semnu;int creat_sems(int nums); int get_sems(); int init_sems(int semid,int which); int destory_sem(int semid); int P(int semid,int which); int V(int semid,int which);#endifcomm.c
#include"comm.h"static int comm_sems(int nums,int flags) {key_t k = ftok(PATHNAME,PROJ_ID);int semid=semget(k,nums,flags);if(semid<0){perror("semget");}return semid;}int creat_sems(int nums) {return comm_sems(nums,IPC_CREAT|IPC_EXCL|0666); }int get_sems() {return comm_sems(0,IPC_CREAT); }int init_sems(int semid,int which) {_semnu __semun;__semun.val=1;if(semctl(semid,which,SETVAL,__semun)<0){perror("semctl");return -1;}return 0; }int destory_sem(int semid) {if(semctl(semid,0,IPC_RMID,NULL)<0){return -1;}return 0; }static int comm_op(int semid,int which,int op) {struct mysembuf sbuf; // memset();sbuf.sem_op=op;sbuf.sem_flg=0;sbuf.sem_num=which;return semop(semid,&sbuf,1); }int P(int semid,int which) {return comm_op(semid,which,-1); } int V(int semid,int which) {return comm_op(semid,which,1); } #include"comm.h"int main() {int semid=creat_sems(1);init_sems(semid,0);pid_t id=fork();if(id<0){perror("fork");}if(id==0){//childwhile(1){int _semid=get_sems();P(_semid,0);printf("A");usleep(12345);fflush(stdout);printf("A");usleep(35412);fflush(stdout);V(_semid,0);}}else{//fatherwhile(1){P(semid,0);printf("B");usleep(32345);fflush(stdout);printf("B");usleep(28712);fflush(stdout);V(semid,0);}wait(NULL);desrory_sems(semid);}return 0; }
當操作信號量(semop)時,sem_flg可以設置SEM_UNDO標識;SEM_UNDO用于將修改的信號量值在進程正常退出(調用exit退出或main執行完)或異常退出(如段異常、除0異常、收到KILL信號等)時歸還給信號量。
如信號量初始值是20,進程以SEM_UNDO方式操作信號量減2,減5,加1;在進程未退出時,信號量變成20-2-5+1=14;在進程退出時,將修改的值歸還給信號量,信號量變成14+2+5-1=20。
總結
以上是生活随笔為你收集整理的对linux信号量的理解以及实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux信号量常用操作表
- 下一篇: Linux使用strlen编译,strl