[Linux]信号量
生活随笔
收集整理的這篇文章主要介紹了
[Linux]信号量
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
信號(hào)量是一個(gè)計(jì)數(shù)器,用于為多個(gè)進(jìn)程提供對(duì)共享數(shù)據(jù)對(duì)象的訪問。
在信號(hào)量上只有三種操作可以進(jìn)行,初始化、遞增和增加,這三種操作都是原子操作。遞減操作可以用于阻塞一個(gè)進(jìn)程,增加操作用于解除阻塞一個(gè)進(jìn)程。
為了獲得共享資源,需要測(cè)試信號(hào)量,若信號(hào)量為正,則進(jìn)程可以使用該資源,這時(shí)信號(hào)量值減一。否則信號(hào)量值為0,進(jìn)程進(jìn)入休眠狀態(tài)。當(dāng)進(jìn)程不再使用由一個(gè)信號(hào)量控制的共享資源時(shí),信號(hào)量值加一。如果有正在休眠的進(jìn)程,則喚醒它們。常用的信號(hào)量的形式為二元信號(hào)量。即是原子性的。
信號(hào)量的功能:負(fù)責(zé)數(shù)據(jù)操作的互斥、同步等功能。本質(zhì)上是一種數(shù)據(jù)操作鎖。
我們?yōu)槭裁匆褂眯盘?hào)量呢?為了防止出現(xiàn)因多個(gè)程序同時(shí)訪問一個(gè)共享資源而引發(fā)的一系列問題,我們需要一種方法,它可以通過生成并使?用令牌來授權(quán),在任一時(shí)刻只能有一個(gè)執(zhí)行線程訪問代碼的臨界區(qū)域。臨界區(qū)域是指執(zhí)行數(shù)據(jù)更新的代碼需要獨(dú)占式地執(zhí)行。而信號(hào)量就可以提供這樣的一種訪問機(jī)制,讓一個(gè)臨界區(qū)同一時(shí)間只有一個(gè)線程在訪問它,也就是說信號(hào)量是用來協(xié)調(diào)進(jìn)程對(duì)共享資源的訪問的。其中共享內(nèi)存的使用就要用到信號(hào)量。
信號(hào)量只能進(jìn)行兩種操作等待和發(fā)送信號(hào),PV操作,即P(sv)和V(sv),P申請(qǐng)資源,則將可用資源數(shù)-1,V釋放資源,則將可用資源數(shù)+1。
內(nèi)核為每個(gè)信號(hào)量集合維護(hù)著一個(gè)semid_ds結(jié)構(gòu):
struct semid_ds {struct ipc_perm sem_perm;unsigned short sem_nsems; //該集合的信號(hào)量數(shù)目time_t sem_otime;time_t sem_ctime; };每個(gè)信號(hào)量都有一個(gè)無名的結(jié)構(gòu):
unsigned short semval; /* semaphore value */unsigned short semzcnt; /* # waiting for zero */unsigned short semncnt; /* # waiting for increase */pid_t sempid; /* process that did last op */當(dāng)我們想使用信號(hào)量時(shí),首先要通過調(diào)用函數(shù)semget來獲得一個(gè)信號(hào)量ID:
#include<sys/sem.h> int semget(key_t key,int nsems,int flag); //成功,返回信號(hào)量ID,出錯(cuò)返回-1semctl函數(shù)包含了多種信號(hào)量操作
int semctl(int semid,int semnum,int cmd,.../* union semun arg */); //第四個(gè)參數(shù)是可選的,取決于請(qǐng)求的命令,如果使用該參數(shù),則其類型是semun,它是多個(gè)命令特定的聯(lián)合。 union semun {int val; //for SETVAL;struct semid_ds *buf; //for IPC_STAT and IPC_SET;unsigned short *array; //for GETALL and SETALL };我們通常使用的:IPC_RMID:從系統(tǒng)中刪除該信號(hào)量集合
SETVAL:設(shè)置成員semnum的semval值,該值由arg.val指定
函數(shù)semop自動(dòng)執(zhí)行信號(hào)量集合上的操作數(shù)組。
int semop(int semid,struct sembuf semoparray[],size_t nops); //成功,返回0,失敗,返回-1;semoparray是一個(gè)指針,指向由sembuf結(jié)構(gòu)表示的信號(hào)量操作數(shù)組,nops規(guī)定該數(shù)組中操作的數(shù)量 struct sembuf {unsigned short sem_num; //0,1,2,3,....short sem_op; //(負(fù)值,0,正值)-1,0,1short sem_flg; //IPC_NOWAIT,SEM_UNDO };對(duì)集合中每個(gè)成員的操作由相應(yīng)的sem_op值規(guī)定。此值可以為負(fù)值,0,正值。最易于處理的是為正值,說明需要釋放資源,則sem_op的值會(huì)加到信號(hào)量值上,如果指定了undo標(biāo)志,則從此信號(hào)量調(diào)整之后的值上減去sem_op;如果sem_op是負(fù)值,說明需要申請(qǐng)資源,則信號(hào)量會(huì)減去sem_op的絕對(duì)值,如果指定undo標(biāo)志,則sem_op的絕對(duì)值也加到信號(hào)量的調(diào)整值上。sem_op為0,表示調(diào)用進(jìn)程希望等待到該信號(hào)量值為0。
對(duì)于信號(hào)量調(diào)整,如果在進(jìn)程終止時(shí),它占用了經(jīng)由信號(hào)量分配的資源,那么就會(huì)成為一個(gè)問題。無論何時(shí)只要為信號(hào)量操作指定了SEM_UNDO標(biāo)志,然后分配資源(sem_op< 0),那么內(nèi)核就會(huì)記住該特定信號(hào)量,分配給調(diào)用進(jìn)程多少資源。對(duì)每個(gè)操作都指定SEM_UNDO,以處理在未釋放資源條件下進(jìn)程終止的情況。
信號(hào)量主要解決互斥與同步問題,下面舉個(gè)栗子:(實(shí)現(xiàn)父子進(jìn)程輸出成對(duì)AA或BB)
//comm.h #ifndef _COMM_H_ #define _COMM_H_#include<stdio.h> #include<sys/types.h> #include<sys/ipc.h> #include<sys/sem.h>#define PATHNAME "." #define PROJ_ID 0X6666 union semun {int val; /* Value for SETVAL */struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */unsigned short *array; /* Array for GETALL, SETALL */struct seminfo *__buf; };int CreateSem(int nums); int GetSem(int nums); int DestroySem(int semid); int initSem(int semid,int nums,int initval); int SemP(int semid,int who); int SemV(int semid,int who);#endif //_COMM_H_ #include"comm.h"static int CommSemSet(int nsems,int flags) {key_t key = ftok(PATHNAME,PROJ_ID);if(key < 0){//printf("%d\n", key);perror("ftok");return -1;}int semid = semget(key,nsems,flags);if(semid < 0){perror("semget");return -2;}return semid; }int CreateSemSet(int nums) {return CommSemSet(nums,IPC_CREAT|IPC_EXCL|0666); }int GetSem(int nums) {return CommSemSet(nums,IPC_CREAT); } int DestroySem(int semid) {if(semctl(semid,0,IPC_RMID) < 0){perror("semctl");return -1;}return 0; }int initSem(int semid,int nums,int initval) {union semun _un;_un.val = initval;if(semctl(semid,nums,SETVAL,_un) < 0){perror("semctl");return -1;}return 0; }static int CommPV(int semid,int who,int op) {struct sembuf _sem;_sem.sem_num = who;_sem.sem_op = op;_sem.sem_flg = 0;if(semop(semid,&_sem,1) < 0){perror("semop");return -1;} return 0; } int SemP(int semid,int who) {return CommPV(semid,who,-1); } int SemV(int semid,int who) {return CommPV(semid,who,1); } //sem.c #include"comm.h"int main() {int semid = CreateSemSet(1);initSem(semid,0,1); pid_t id = fork();if(id == 0)//child{int _semid = GetSem(0);while(1){SemP(_semid,0);printf("A");fflush(stdout);usleep(123456);printf("A");fflush(stdout);usleep(345678); SemV(_semid,0);}}else{while(1){SemP(semid,0);printf("B");fflush(stdout);usleep(234567);printf("B");fflush(stdout);usleep(456789);// usleep(121212);SemV(semid,0);}wait(NULL);}DestroySem(semid);printf("sem quit!\n");return 0; } //Makefile sem:sem.c comm.cgcc -o $@ $^ .PHONY:clean clean:rm -f sem運(yùn)行結(jié)果:
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的[Linux]信号量的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: dnf什么职业最好
- 下一篇: [Linux]vi/vim下添加多行注释