Linux信号量操作
信號(hào)量簡(jiǎn)介:
在對(duì)于臨界區(qū)資源管理的過(guò)程中,多個(gè)程序同時(shí)訪問(wèn)一個(gè)共享資源經(jīng)常容易引發(fā)一系列問(wèn)題:如死鎖,結(jié)果不唯一等等,在1965年,由荷蘭科學(xué)家E.W.Dijkstra提出了一種新的進(jìn)程同步工具,信號(hào)量及其PV操作。
對(duì)于信號(hào)量的定義是這樣的:
??????????????? 讓多個(gè)進(jìn)程通過(guò)特殊變量展開(kāi)交互,一個(gè)進(jìn)程在某一個(gè)關(guān)鍵點(diǎn)上被迫停止執(zhí)行直至接收到對(duì)應(yīng)的特殊變量值,通過(guò)這一措施,任何復(fù)雜的進(jìn)程交互要求均可得到滿足,這種特殊的變量就是信號(hào)量。
信號(hào)量的種類分為以下2種:
一般信號(hào)量:
?????????????? 設(shè)s為一個(gè)記錄型數(shù)據(jù)結(jié)構(gòu),其中value為整型變量,系統(tǒng)初始化時(shí)為其賦值,PV操作的原語(yǔ)描述如下:
?????????????? P(s):將信號(hào)量value值減1,若結(jié)果小于0,則執(zhí)行P操作的進(jìn)程被阻塞,若結(jié)果大于等于0,則執(zhí)行P操作的進(jìn)程將繼續(xù)執(zhí)行。
?????????????? V(s):將信號(hào)量的值加1,若結(jié)果不大于0,則執(zhí)行V操作的進(jìn)程從信號(hào)量s有關(guān)的list所知隊(duì)列中釋放一個(gè)進(jìn)程,使其轉(zhuǎn)化為就緒態(tài),自己則繼續(xù)執(zhí)行,若結(jié)果大于0,則執(zhí)行V操作的進(jìn)程繼續(xù)執(zhí)行。
二值信號(hào)量:
????????????? 設(shè)s為一個(gè)記錄型數(shù)據(jù)結(jié)構(gòu),其中分量value僅能取值0或1,二值信號(hào)量的PV操作的原語(yǔ)描述和一般信號(hào)量相同,雖然二值信號(hào)量?jī)H能取值0或1,但可以證明他有著與其他信號(hào)量相同的表達(dá)能力
?????????? ?
當(dāng)然以上都是創(chuàng)造者所給出的定義,對(duì)于實(shí)現(xiàn)方式在不同的平臺(tái)下接口會(huì)有不同,以下的實(shí)現(xiàn)部分均是建立在Linux平臺(tái)下使用C語(yǔ)言編碼實(shí)現(xiàn),主要介紹的也是對(duì)應(yīng)的C接口
上面那些定義總結(jié)起來(lái)可以這樣說(shuō):通過(guò)使用信號(hào)量生成令牌來(lái)授權(quán),在任一時(shí)刻只能有一個(gè)執(zhí)行線程訪問(wèn)代碼的臨界區(qū)域。臨界區(qū)域是指執(zhí)行數(shù)據(jù)更新的代碼需要獨(dú)占式地執(zhí)行。而信號(hào)量就可以提供這樣的一種訪問(wèn)機(jī)制,讓一個(gè)臨界區(qū)同一時(shí)間只有一個(gè)線程在訪問(wèn)它,也就是說(shuō)信號(hào)量是用來(lái)調(diào)協(xié)進(jìn)程對(duì)共享資源的訪問(wèn)的一種手段。
函數(shù)操作:
對(duì)于與信號(hào)量操作有關(guān)的接口,Linux下主要提供了以下幾個(gè)函數(shù),值得注意的是,在Linux下的C接口中,這些函數(shù)的操作對(duì)象都是信號(hào)量值組,也就是一個(gè)信號(hào)量值的鏈表
int?semget(key_t?key,?int?num_sems,?int?sem_flags);
該函數(shù)的作用是創(chuàng)建一個(gè)新信號(hào)量或取得一個(gè)已有信號(hào)量。
第一個(gè)參數(shù)key是整數(shù)值(唯一非零),就是Linux線程操作中經(jīng)常用到的鍵值,可以通過(guò)ftok函數(shù)得到,不相關(guān)的進(jìn)程可以通過(guò)它訪問(wèn)一個(gè)信號(hào)量,它代表程序可能要使用的某個(gè)資源,程序?qū)λ行盘?hào)量的訪問(wèn)都是間接的,程序先通過(guò)調(diào)用semget函數(shù)并提供一個(gè)鍵,再由系統(tǒng)生成一個(gè)相應(yīng)的信號(hào)標(biāo)識(shí)符(semget函數(shù)的返回值),只有semget函數(shù)才直接使用信號(hào)量鍵,所有其他的信號(hào)量函數(shù)使用由semget函數(shù)返回的信號(hào)量標(biāo)識(shí)符。如果多個(gè)程序使用相同的key值,key將負(fù)責(zé)協(xié)調(diào)工作。
第二個(gè)參數(shù)num_sems指定需要的信號(hào)量數(shù)目,它的值幾乎總是1。
第三個(gè)參數(shù)sem_flags是一組標(biāo)志,當(dāng)想要當(dāng)信號(hào)量不存在時(shí)創(chuàng)建一個(gè)新的信號(hào)量,可以和值IPC_CREAT做按位或操作。設(shè)置了IPC_CREAT標(biāo)志后,即使給出的鍵是一個(gè)已有信號(hào)量的鍵,也不會(huì)產(chǎn)生錯(cuò)誤。而IPC_CREAT | IPC_EXCL則可以創(chuàng)建一個(gè)新的,唯一的信號(hào)量,如果信號(hào)量已存在,返回一個(gè)錯(cuò)誤。
semget函數(shù)成功返回一個(gè)相應(yīng)信號(hào)標(biāo)識(shí)符(非零),失敗返回-1
int semop(int sem_id, struct sembuf *sops, size_t nsops);
該函數(shù)的作用是改變信號(hào)量的值,其實(shí)就是為了信號(hào)量的PV操作而準(zhǔn)備的,這個(gè)函數(shù)可以講的地方比較多,下面會(huì)詳細(xì)介紹:
函數(shù)的第一個(gè)參數(shù) semid 為信號(hào)量集的標(biāo)識(shí)符;
第2個(gè)參數(shù) sops 指向進(jìn)行操作的結(jié)構(gòu)體數(shù)組的首地址,在 semop 的第二個(gè)參數(shù) sops 指向的結(jié)構(gòu)體數(shù)組中,每個(gè) sembuf 結(jié)構(gòu)體對(duì)應(yīng)一個(gè)特定信號(hào)的操作。因此對(duì)信號(hào)量進(jìn)行操作必須熟悉該數(shù)據(jù)結(jié)構(gòu),該結(jié)構(gòu)定義在 linux/sem.h,如下所示:
[cpp]?view plain?copy
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)志
};
對(duì)于該結(jié)構(gòu)中各個(gè)成員都具有特殊的含義,具體含義的介紹如下:
sem_op 參數(shù):
sem_op > 0?信號(hào)加上 sem_op 的值,表示進(jìn)程釋放控制的資源;
sem_op = 0?如果sem_flg沒(méi)有設(shè)置IPC_NOWAIT,則調(diào)用進(jìn)程進(jìn)入睡眠狀態(tài),直到信號(hào)量的值為0;否則進(jìn)程不會(huì)睡眠,直接返回 EAGAIN
sem_op < 0?信號(hào)加上 sem_op 的值。若沒(méi)有設(shè)置?IPC_NOWAIT?,則調(diào)用進(jìn)程阻
塞,直到資源可用;否則進(jìn)程直接返回EAGAIN
sem_flg 參數(shù):
該參數(shù)可設(shè)置為 IPC_NOWAIT 或 SEM_UNDO 兩種狀態(tài)。只有將 sem_flg 指定為 SEM_UNDO 標(biāo)志后,semadj (所指定信號(hào)量針對(duì)調(diào)用進(jìn)程的調(diào)整值)才會(huì)更新。 此外,如果此操作指定SEM_UNDO,系統(tǒng)更新過(guò)程中會(huì)撤消此信號(hào)燈的計(jì)數(shù)(semadj)。此操作可以隨時(shí)進(jìn)行---它永遠(yuǎn)不會(huì)強(qiáng)制等待的過(guò)程。調(diào)用進(jìn)程必須有改變信號(hào)量集的權(quán)限。
sem_flg公認(rèn)的標(biāo)志是 IPC_NOWAIT 和 SEM_UNDO。如果操作指定SEM_UNDO,當(dāng)該進(jìn)程終止時(shí)它將會(huì)自動(dòng)撤消。
第3個(gè)參數(shù) nsops 指出將要進(jìn)行操作的信號(hào)的個(gè)數(shù)。semop 函數(shù)調(diào)用成功返回 0,失敗返回 -1。
該函數(shù)所做的對(duì)于信號(hào)量的操作都是原子操作,即整個(gè)行為是一個(gè)整體,是不可打斷的。所有操作是否可以立即執(zhí)行取決于在個(gè)人sem_flg領(lǐng)域的IPC_NOWAIT標(biāo)志的存在。
int semctl(int sem_id, int sem_num, int command, ...);
函數(shù)的第一個(gè)參數(shù)?semid?為信號(hào)量集的標(biāo)識(shí)符;函數(shù)的第二個(gè)參數(shù)sem_num則是表示即將要進(jìn)行操作的信號(hào)量的編號(hào),即信號(hào)量集合的索引值,其中第一個(gè)信號(hào)量的索引值為0。
函數(shù)的第3個(gè)參數(shù)command代表將要在集合上執(zhí)行的命令,其取值含義如下,通常用特定的宏代替:
IPC_STAT:獲取某個(gè)信號(hào)量集合的semid_ds結(jié)構(gòu),并將其儲(chǔ)存在semun聯(lián)合體的buf參數(shù)所指的地址之中
IPC_SET:設(shè)置某個(gè)集合的semid_ds結(jié)構(gòu)的ipc_perm成員的值,該命令所取的值是從semun聯(lián)合體的buf參數(shù)中取到的
IPC_RMID:從內(nèi)核刪除該信號(hào)量集合
GETALL:用于獲取集合中所有信號(hào)量的值,整數(shù)值存放在無(wú)符號(hào)短整數(shù)的一個(gè)數(shù)組中,該數(shù)組有聯(lián)合體的array成員所指定
GETNCNT:返回當(dāng)前正在等待資源的進(jìn)程的數(shù)目
GETPID:返回最后一次執(zhí)行PV操作(semop函數(shù)調(diào)用)的進(jìn)程的PID
GETVAL:返回集合中某個(gè)信號(hào)量的值
GETZCNT:返回正在等待資源利用率達(dá)到百分之百的進(jìn)程的數(shù)目
SETALL:把集合中所有信號(hào)量的值,設(shè)置為聯(lián)合體的array成員所包含的對(duì)應(yīng)值
SETVAL:將集合中單個(gè)信號(hào)量的值設(shè)置為聯(lián)合體的val成員的值
其中semun聯(lián)合體的結(jié)構(gòu)如下:
[cpp]?view plain?copy對(duì)于該函數(shù),只有當(dāng)command取某些特定的值的時(shí)候,才會(huì)使用到第4個(gè)參數(shù),第4個(gè)參數(shù)它通常是一個(gè)union semum結(jié)構(gòu),定義如下:
[cpp]?view plain?copy當(dāng)執(zhí)行SETVAL命令時(shí)用到這個(gè)成員,他用于指定要把信號(hào)量設(shè)置成什么值,涉及成員:val
在命令I(lǐng)PC_STAT/IPC_SET中使用,它代表內(nèi)核中所使用內(nèi)部信號(hào)量數(shù)據(jù)結(jié)構(gòu)的一個(gè)復(fù)制 ,涉及成員:buf
在命令GETALL/SETALL命令中使用時(shí),他代表指向整數(shù)值一個(gè)數(shù)組的指針,在設(shè)置或獲取集合中所有信號(hào)量的值的過(guò)程中,將會(huì)用到該數(shù)組,涉及成員:array
剩下的還有一些用法都將在系統(tǒng)內(nèi)核中的信號(hào)量代碼使用,應(yīng)用程序開(kāi)發(fā)中使用很少,這里也就不介紹了。
實(shí)現(xiàn)樣例:
這里列舉一個(gè)別人的樣例,主要是為了展示信號(hào)量控制進(jìn)程的操作代碼如下:
[html]?view plain?copy
[html]?view plain?copy
總結(jié)
以上是生活随笔為你收集整理的Linux信号量操作的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 电脑重装系统 Win11 如何打开Dir
- 下一篇: icem二维非结构网格划分_ICEM蜗壳