網(wǎng)站建設(shè)流程簡圖獨(dú)立站推廣
前面學(xué)習(xí)了pipe,fifo,共享內(nèi)存,信號(hào)。
本章將講述信號(hào)量。
一、什么是信號(hào)量/信號(hào)量集?
1.什么是信號(hào)量
????????信號(hào)量是一個(gè)計(jì)數(shù)器。信號(hào)量用于實(shí)現(xiàn)進(jìn)程間的同步和互斥。而可以取多個(gè)正整數(shù)的信號(hào)量被稱為通用信號(hào)量。
??????? 對(duì)信號(hào)量的使用場景的解讀
??????? 房間:臨界資源(操作系統(tǒng)中的多個(gè)進(jìn)程,他們共享各種資源,然而很多資源1次只能供1個(gè)進(jìn)程使用。這種1次僅允許1個(gè)進(jìn)程使用的資源稱為臨界資源。)
????????鑰匙:信號(hào)量
????????只有拿到鑰匙才能進(jìn)入房間(如果進(jìn)程A正在訪問臨界資源,則不允許進(jìn)程B訪問臨界資源)。
2.什么是信號(hào)量集
linux中的信號(hào)量不止一個(gè),有很多個(gè)。
二、P操作和V操作
p操作:取鑰匙
V操作:放回鑰匙
三、相關(guān)API
1.semget-----創(chuàng)建信號(hào)量
通過semget 來創(chuàng)建信號(hào)量或獲取一個(gè)已有的信號(hào)量,
函數(shù)原型:#include <sys/sem.h>int semget(key_t key, int nsems, int semflg);參數(shù):key 信號(hào)量的關(guān)鍵字,可以通過ftok() 創(chuàng)建,詳細(xì)看 進(jìn)程間通信——消息隊(duì)列nsems 信號(hào)量的數(shù)目(比如1代表信號(hào)量集合中有1個(gè)信號(hào)量)。如果是創(chuàng)建新的信號(hào)量,必須要指定nsems。如果是引用現(xiàn)有的信號(hào)量,nsems指定為0。shmflg 有兩個(gè)選項(xiàng),IPC_CREAT 表示內(nèi)核中沒有此信號(hào)量則創(chuàng)建它。IPC_EXCL 當(dāng)和IPC_CREAT一起使用時(shí),如果信號(hào)量已經(jīng)存在,則返回錯(cuò)誤。返回值:
成功返回標(biāo)識(shí)符,否則返回-1。通過errno和perror函數(shù)可以查看錯(cuò)誤信息。注意:
通過nsems 可以看出,創(chuàng)建一個(gè)信號(hào)量,里面可能包含多個(gè)信號(hào)量值。該信號(hào)量就相當(dāng)于一個(gè)集合。該值表明有多少個(gè)共享資源單位可供共享應(yīng)用。下面semctl 也是通過nsems 中的某一個(gè)進(jìn)行操作。
如果nsems 設(shè)為1,該信號(hào)量又被稱為二元信號(hào)量(binary semaphore)
2.semctl------初始化信號(hào)量
這個(gè)函數(shù)可以有3個(gè)參數(shù)或者4個(gè)參數(shù)
#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>int semctl(int semid, int semnum, int cmd, ...);@semid:信號(hào)量數(shù)組標(biāo)識(shí)@semnum:要操作的信號(hào)量數(shù)組的信號(hào)量下標(biāo)(比如:0代表操作第0個(gè)信號(hào)量)(該下表和數(shù)組下標(biāo)一致,比如有1個(gè)信號(hào)量,則下表為0,有2個(gè)信號(hào)量則下表為1)@cmd:IPC_STAT 查詢此信號(hào)量數(shù)組的數(shù)據(jù)存入arg.buf(buf為struct semid_ds結(jié)構(gòu)體指針)IPC_RMID 刪除指定semid的信號(hào)量數(shù)組GETVAL 獲取信號(hào)量的當(dāng)前值,SETVAL 設(shè)置信號(hào)量的值,初始化要用的命令
————————————————
版權(quán)聲明:本文為CSDN博主「abist」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/abist/article/details/117606809This function has three or four arguments, depending on cmd. Whenthere are four, the fourth has the type union semun. The calling pro‐gram must define this union as follows:當(dāng)使用4個(gè)參數(shù)時(shí),要定義下面的聯(lián)合體union semun {int val; /* 鑰匙的數(shù)量 */struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */unsigned short *array; /* Array for GETALL, SETALL */struct seminfo *__buf; /* Buffer for IPC_INFO(Linux-specific) */};
3.semop---信號(hào)量的操作
int semop(int semid,struct sembuf *sops,size_t nsops);函數(shù)的參數(shù)
參數(shù)semid 為信號(hào)量集的標(biāo)識(shí)符;
參數(shù) sops 指向進(jìn)行操作的結(jié)構(gòu)體數(shù)組的首地址;
參數(shù) nsops 指出將要進(jìn)行操作的信號(hào)的個(gè)數(shù)。返回值
semop 函數(shù)調(diào)用成功返回 0,失敗返回 -1。sembuf 結(jié)構(gòu)體對(duì)應(yīng)一個(gè)特定信號(hào)的操作。
該結(jié)構(gòu)定義在 linux/sem.h,如下所示:
struct sembuf{unsigned short sem_num; //信號(hào)在信號(hào)集中的索引,0代表第一個(gè)信號(hào),1代表第二個(gè)信號(hào) short sem_op; //操作類型short sem_flg; //操作標(biāo)志
};
sem_num標(biāo)識(shí)信號(hào)量集中的第幾個(gè)信號(hào)量,0表示第1個(gè),1表示第2個(gè),nsems - 1表示最后一個(gè)。sem_op標(biāo)識(shí)對(duì)信號(hào)量的所進(jìn)行的操作類型。對(duì)信號(hào)量的操作有三種類型:sem_op > 0,對(duì)該信號(hào)量執(zhí)行掛出操作,掛出的值由sem_op決定,系統(tǒng)會(huì)把sem_op的值加到該信號(hào)量的當(dāng)前值semval(參考文章開頭關(guān)于每個(gè)信號(hào)量結(jié)構(gòu)的定義)上。如果sem_flag指定了SEM_UNDO(還原)標(biāo)志,那么相應(yīng)信號(hào)量的semadj值會(huì)減掉sem_op的值。下面會(huì)說明semadj的含義。sem_op < 0,對(duì)該信號(hào)量執(zhí)行等待操作,當(dāng)信號(hào)量的當(dāng)前值semval >= -sem_op時(shí),semval減掉sem_op的絕對(duì)值,為該線程分配對(duì)應(yīng)數(shù)目的資源。如果指定SEM_UNDO,相應(yīng)信號(hào)量的semadj就加上sem_op的絕對(duì)值。 當(dāng)semval < -sem_op時(shí),相應(yīng)信號(hào)量的semncnt就加1,調(diào)用線程被阻塞,直到semval >= -sem_op,當(dāng)此條件滿足時(shí),調(diào)用線程被喚醒,執(zhí)行相應(yīng)的分配操作,然后semncnt減去1。sem_op = 0,表示調(diào)用者希望semval變?yōu)?。如果為0則立即返回,如果不為0,相應(yīng)信號(hào)量的semzcnt加1,調(diào)用調(diào)用線程被阻塞。sem_flag:信號(hào)量操作的屬性標(biāo)志,如果為0,表示正常操作,如果為IPC_WAIT,使對(duì)信號(hào)量的操作時(shí)非阻塞的。即指定了該標(biāo)志,調(diào)用線程在信號(hào)量的值不滿足條件的情況下不會(huì)被阻塞,而是直接返回-1,并將errno設(shè)置為EAGAIN。如果為SEM_UNDO,那么將維護(hù)進(jìn)程對(duì)信號(hào)量的調(diào)整值,以便進(jìn)程結(jié)束時(shí)恢復(fù)信號(hào)量的狀態(tài)。下面解釋一下與單個(gè)信號(hào)量相關(guān)的幾個(gè)值:
semval:信號(hào)量的當(dāng)前值,在文章開頭信號(hào)量的結(jié)構(gòu)中已提到。
semncnt:等待semval變?yōu)榇笥诋?dāng)前值的線程數(shù)。在文章開頭信號(hào)量的結(jié)構(gòu)中已提到。
semzcnt:等待semval變?yōu)?的線程數(shù)。在文章開頭信號(hào)量的結(jié)構(gòu)中已提到。
semadj:指定信號(hào)量針對(duì)某個(gè)特定進(jìn)程的調(diào)整值。只有sembuf結(jié)構(gòu)的sem_flag指定為SEM_UNDO后,semadj才會(huì)隨著sem_op而更新。講簡單一點(diǎn):對(duì)某個(gè)進(jìn)程,在指定SEM_UNDO后,對(duì)信號(hào)量semval值的修改都會(huì)反應(yīng)到semadj上,當(dāng)該進(jìn)程終止的時(shí)候,內(nèi)核會(huì)根據(jù)semadj的值,重新恢復(fù)信號(hào)量之前的值。
4.P操作(取鑰匙)
自定義
要利用semop()來實(shí)現(xiàn)void PGetKey(int id)//id是semget()的返回值
{struct sembuf set;//該結(jié)構(gòu)定義在 linux/sem.hset.sem_num = 0;//這是下標(biāo),0代表第1個(gè)信號(hào);set.sem_op = -1;//是指對(duì)鑰匙數(shù)量減1;set.sem_flag = SEM_UNDO;//配置為SEM_UNDO當(dāng)進(jìn)程終止時(shí),自動(dòng)取消對(duì)該鑰匙的操作semop(id,&set,1);
}
5.V操作(放回鑰匙)
自定義
要利用semop()來實(shí)現(xiàn)void VputbackKey(int id)//id是semget()的返回值
{struct sembuf set;//該結(jié)構(gòu)定義在 linux/sem.hset.sem_num = 0;//這是下標(biāo),0代表第1個(gè)信號(hào);set.sem_op = 1;//是指對(duì)鑰匙數(shù)量加1;set.sem_flag = SEM_UNDO;//配置為SEM_UNDO當(dāng)進(jìn)程終止時(shí),自動(dòng)取消對(duì)該鑰匙的操作semop(id,&set,1);
}
四、實(shí)驗(yàn)
實(shí)驗(yàn)要求:
最初狀態(tài):沒有鑰匙。
創(chuàng)建子進(jìn)程后,
????????讓父進(jìn)程取鑰匙(由于鑰匙的數(shù)量為0,所以父進(jìn)程取鑰匙的動(dòng)作被阻塞),使用結(jié)束(訪問臨界資源)后打印“father process”,放回鑰匙。
????????讓子進(jìn)程放鑰匙,然后打印“child process”。
(由于最初父進(jìn)程會(huì)被阻塞,所以程序運(yùn)行結(jié)果是先打印"child process"后打印“father process")
思路分析
(1)生成鍵值
(2)創(chuàng)建信號(hào)量
(3)初始化信號(hào)量:定義聯(lián)合體-->鑰匙的個(gè)數(shù)置為0--->使用semctl初始化信號(hào)量
(4)創(chuàng)建子進(jìn)程
(5)判斷父子進(jìn)程
??????????????? --->在父進(jìn)程里:取鑰匙,打印“father? process”,放回鑰匙。
??????????????? ---->在子進(jìn)程里:放鑰匙,打印“child process"
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>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; /* Buffer for IPC_INFO(Linux-specific) */};void VputbackKey(int id)//id是semget()的返回值
{struct sembuf set;//該結(jié)構(gòu)定義在 linux/sem.hset.sem_num = 0;//這是下標(biāo),0代表第1個(gè)信號(hào);set.sem_op = 1;//是指對(duì)鑰匙數(shù)量加1;set.sem_flg = SEM_UNDO;//配置為SEM_UNDO當(dāng)進(jìn)程終止時(shí),自動(dòng)取消對(duì)該鑰匙的操作semop(id,&set,1);printf("put back key\n");
}void PGetKey(int id)//id是semget()的返回值
{struct sembuf set;//該結(jié)構(gòu)定義在 linux/sem.hset.sem_num = 0;//這是下標(biāo),0代表第1個(gè)信號(hào);set.sem_op = -1;//是指對(duì)鑰匙數(shù)量減1;set.sem_flg = SEM_UNDO;//配置為SEM_UNDO當(dāng)進(jìn)程終止時(shí),自動(dòng)取消對(duì)該鑰匙的操作semop(id,&set,1);printf("get key\n");
}int main()
{key_t key;int semid;key = ftok(".",2);semid = semget(key,1,IPC_CREAT|0666);union semun initsem;initsem.val = 0;semctl(semid,0,SETVAL,initsem);int pid = fork();if(pid > 0){PGetKey(semid);printf("father process\n");VputbackKey(semid);}else if(pid == 0){VputbackKey(semid);printf("child process\n");}else{printf("fork error\n");}return 0;
}
五、如何銷毀鑰匙
semctl(semid,0,IPCIPC_RMID);//3個(gè)參數(shù)