進程間通信---POSIX信號量實現(xiàn)機制

1. 什么是POSIX

????首先,我們需要弄清楚的第一個問題是,什么是POSIX?

? ? POSIX是可移植操作系統(tǒng)接口(Portable Operating System Interface)的縮寫,是IEEE為了在各種UNIX操作系統(tǒng)(POSIX最后一個字母來自UNIX的X)上運行軟件而定義的一系列API標準總稱,正式稱呼為IEEE 1003,目前,此協(xié)議已經(jīng)被大多數(shù)操作系統(tǒng)所支持。

? ? 現(xiàn)在,我們弄明白了什么是POSIX,所謂的POSIX就是一套API的標準,各個操作系統(tǒng)廠商如果支持POSIX協(xié)議,那么此協(xié)議下的API都要按照協(xié)議的要求來實現(xiàn),以方便軟件的系統(tǒng)移植工作。

? ? 那么,什么是POSIX信號量?

? ? 顧名思義,POSIX信號量,就是在POSIX標準下實現(xiàn)的信號量,至于什么是信號量,請查看《進程間通信----信號量》一節(jié)。

? ? 在軟件的開發(fā)過程中,如果你需要使用某個POSIX標準定義的接口,那么首先你需要先確認目標工作機器的操作系統(tǒng)是否支持POSIX標準(目前大多數(shù)的操作系統(tǒng)都支持)。

2. POSIX信號量的分類

? ? POSIX信號量分別為有名信號量和無名信號量兩類:

2.1 有名信號量

? ? 有名信號量通過一個名字來作為標識,名字的格式為"/somename",這個名字的長度為MAX_NAME - 4(NAME_MAX是一個宏定義,大小為,定義在頭文件 中,各個系統(tǒng)都會實現(xiàn)這個宏的定義,抱歉,本人未查詢到NAME_MAX的相關(guān)定義),信號量名字以'/'為開始,以'\0'字符為結(jié)尾,并且中間字符串中不能再有'/'。

? ? 兩個進程之間可以通過sem_open函數(shù)來操作同一個有名信號量,本小結(jié)中出現(xiàn)的函數(shù)會在第4章節(jié)中進行詳細的介紹。

? ? 用戶可以通過sem_open函數(shù)接口創(chuàng)建一個新的有名信號量或者打開一個已有的有名信號量。有名信號量被打開之后,進程或線程(有名信號量一般應(yīng)用在進程之間的同步控制,下小節(jié)介紹的無名信號量一般應(yīng)用在線程之間的同步控制)可以通過sem_post或者sem_wait接口進行信號量操作。當一個進程不在使用有名信號量后,可以通過sem_close接口來關(guān)閉它,當系統(tǒng)中所有的進程都不在使用某個有名信號量后,可以通過sem_unlink接口將它從系統(tǒng)中移除。

2.2 無名信號量

? ? 與有名信號量不同的,無名信號量并沒有名字,那么如何去標識一個無名信號量呢,POSIX標準只是規(guī)定,無名信號量必須被放置在一段可以被多線程或者多進程所共享的內(nèi)存區(qū)域中。

? ? 無名信號量有一個共享屬性,分為線程共享屬性和進程共享屬性兩類。

? ? 一個線程共享屬性(無名信號量的共享屬性在后續(xù)章節(jié)中會介紹,它分為線程共享屬性和進程共享屬性兩類)的無名信號量必須被放置在一個可以被進程中所有線程所共享的內(nèi)存區(qū)域中,比如全局變量。

? ? 一個進程共享屬性的無名信號量必須被放置在共享內(nèi)存區(qū)域,系統(tǒng)V(System V,后續(xù)文章會對System V標準進行介紹)通過shmget(他是一個System V標準定義的一個接口)接口實現(xiàn)共享內(nèi)存,POSIX通過shm_open接口來實現(xiàn)共享內(nèi)存區(qū)域。

? ? 無名信號量在使用之前必須通過sem_init接口進行初始化,之后可以通過sem_wait和sem_post接口進行操作,當不再使用無名信號量,或者放置無名信號的區(qū)域被釋放之前,用戶都應(yīng)該通過sem_destroy接口來釋放無名信號量。

3. POSIX信號量實現(xiàn)了哪些接口函數(shù)

? ? 本章節(jié)主要針對第二章節(jié)中涉及到的POSIX標準下的信號量接口進行詳細的描述,依據(jù)《Linux用戶手冊release4.04》。

3.1 有名信號量接口說明

? ? 3.1.1?sem_open

????我們在使用一個有名信號量的時候,第一步就是要創(chuàng)建或者打開一個已有的有名信號量,我們需要的函數(shù)就是sem_open,函數(shù)原型如下:

? ? (1)sem_t *sem_open(const char *name, int oflag);

? ? (2)sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);

? ? 函數(shù)功能:創(chuàng)建并初始化或者打開一個已有的有名信號量。

? ? 返回值:成功的話,函數(shù)返回有名信號量的地址,這個返回值會在其它一些相關(guān)的函數(shù)中作為操作對象的索引而被使用,比如sem_post、sem_wait等函數(shù)。失敗的話返回SEM_FAILED。我們在使用此函數(shù)的時候,都應(yīng)該去檢查返回值,以確保我們的調(diào)用是正確的。

? ? 參數(shù):

? ? ? ? name:有名信號量的名字,在2.1章節(jié)進行過詳細的描述,這里不再重復(fù)。

? ? ? ? oflag:此參數(shù)控制函數(shù)內(nèi)部的具體操作行為,如果oflag參數(shù)里有O_CREAT位,那么如果此信號量不存在則創(chuàng)建此信號量并初始化它,此時信號量的user ID被設(shè)置為調(diào)用進程的有效user ID,group ID被設(shè)置成調(diào)用進程的group ID,如果oflag參數(shù)里既包含O_CREAT位也包含O_EXCL位,那么如果指定名字的信號量已存在那么就會返回錯誤。

? ? ? ? mode:如果oflag參數(shù)里面包含O_CREAT位,那么我們就應(yīng)用調(diào)用第二個同名函數(shù)(2),并將操作權(quán)限設(shè)置到mode參數(shù)里面,只要用戶想使用此信號量,那么讀寫權(quán)限都應(yīng)該設(shè)置到mode參數(shù)里(這里的讀寫權(quán)限,跟我們常用的打開文件函數(shù)open一樣,比如:O_RDONLY O_WRONLY O_RDWR等)。

? ? ? ? value:想要設(shè)置的有名信號量的初始值,無符號整形值,即不小于0的整形值。

????如果O_CREAT被設(shè)置,并且信號量已經(jīng)存在,那么系統(tǒng)會自動忽略mode跟value參數(shù)。

? ? 我們在調(diào)用sem_open函數(shù)的時候,一般有這么幾種使用方法:

? ? ? ? ? ? (1)新建一個有名信號量 sem_open("/name", O_CREAT, O_RDWR, 1);

????????????成功:若果有名信號量不存在,則創(chuàng)建一個名字為”/name"可讀可寫初始value為1的有名信號量。如果信號量存在,則返回已有有名信號量的地址,并且忽略mode跟value參數(shù)的值。

? ? ? ? ? ? 失?。悍祷豐EM_FAILED。

? ? ? ? ? ? (2)必須新建一個信號量 sem_open("/name", O_CREAT|O_EXCL, O_RW, ?1);

? ? ? ? ? ? 若信號量不存在,則創(chuàng)建一個名字為”/name"可讀可寫初始value為1的有名信號量,如果信號量存在,則失敗返回SEM_FAILED。

3.2 無名信號量接口說明

? ? 3.2.1 sem_init

? ? 函數(shù)原型:

? ??int sem_init(sem_t *sem, int pshared, unsigned int value)

? ? 函數(shù)sem_init的作用是在參數(shù)sem指向的地址上初始化一個無名信號量,其value值由參數(shù)value指定。

????參數(shù)pshared指定此無名信號量實在進程之間被共享使用,還是在線程之間被共享使用。

? ? 如果參數(shù)pshared等0,那么此信號量只能在一個進程內(nèi)的線程之間共享使用,所以,它應(yīng)該被實現(xiàn)在可以被所有線程都能訪問到的地址上,如全局變量或者動態(tài)分配在堆上。

? ? 如果參數(shù)pshared是一個非0值,那么此信號量被多個進程共享使用,它應(yīng)該被實現(xiàn)在共享內(nèi)存里,或者父子進程之間使用。

? ? 注意:如果使用此函數(shù)去初始化一個已經(jīng)被初始化的信號量,此函數(shù)的行為是未被定義的。

? ? 返回值:成功返回0,失敗返回-1.

? ? 3.2.2 sem_destroy

? ? 函數(shù)原型:

? ??int sem_destroy(sem_t *sem)

? ? 描述:函數(shù)sem_destroy的作用是銷毀由參數(shù)sem指向的無名信號量。只有用sem_init初始化的無名信號量才可以用此函數(shù)來銷毀,并且程序開發(fā)人員也應(yīng)該使用此函數(shù)去銷毀這個無名信號量。

? ? 當銷毀一個無名信號量時,其它應(yīng)為調(diào)用sem_wait函數(shù)而堵塞在此信號量上的進程或線程的行為是未定義的。

? ? 當使用一個已經(jīng)被銷毀的信號量時,后果也是未定義的。

? ? 返回值:成功返回0,失敗返回-1

? ? 注意:當銷毀一段內(nèi)存時,如果此內(nèi)存上有已經(jīng)初始化的無名信號量,那么在銷毀內(nèi)存之前,應(yīng)該先調(diào)用sem_destroy函數(shù)來銷毀信號量。

3.3 信號量通用接口說明

?3.3.1 sem_wait

? ? 函數(shù)原型:

? ? (1)int sem_wait(sem_t *sem);

? ? (2)int sem_trywait(sem_t *sem);

? ? (3)int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

sem_wait系列操作,即我們所說的PV操作中的P操作,目的是鎖住一個信號量。

? ? (1) sem_wait函數(shù)將由參數(shù)sem指定的信號量減一,即上鎖操作。如果信號量的值大于0,那么執(zhí)行減一操作,并且函數(shù)立即返回。如果信號量當前的值為0,那么調(diào)用進程會一直阻塞直到信號量變成大于0(由其它進程執(zhí)行了sem_post操作,sem_post函數(shù)會在3.1.3章節(jié)進行箱明細描述,它執(zhí)行信號量加一操作)或者被信號中斷此調(diào)用。

? ? (2)sem_trywait函數(shù)同sem_wait函數(shù)的作用一樣,不同是如果不能立即執(zhí)行加一操作,則調(diào)用進程不會堵塞而是返回一個錯誤,errno會被設(shè)置成EAGAIN。

? ? (3)sem_timedwait函數(shù)同sem_wait函數(shù)的作用一樣,不同是如果不能立即執(zhí)行加一操作,則調(diào)用進程會堵塞一定的時間段,這個時間段由函數(shù)參數(shù)abs_timeout指定。如果在指定的時間內(nèi)信號量仍不能被鎖住,則函數(shù)返回超時錯誤,errno會被設(shè)置成ETIMEDOUT。如果信號量的減一操作可以被立即執(zhí)行,則此函數(shù)永遠都不會返回超時錯誤,并且參數(shù)abs_timeout的有效性也不會被檢查。

? ? 返回值:成功返回0,失?。盒盘柫康闹挡蛔?,返回-1。

????3.3.2 sem_post

? ? 函數(shù)原型:

? ? #include

? ??int sem_post(sem_t *sem)

? ? Link with -pthread

與sem_wait相對應(yīng)的函數(shù)就是sem_post,即我們PV操作里面的V操作。

此函數(shù)將sem指向的信號量解鎖(加一操作),加一操作后如果信號量的值變成大于0,那么另外一個因為調(diào)用sem_wait函數(shù)而被堵塞的進程或者線程將會被喚醒并且去執(zhí)行對信號量的加鎖操作。

返回值:成功返回0;失敗返回-1,并且信號量值不變。

注意:sem_post函數(shù)是異步信號安全的,它可以在一個信號處理函數(shù)中被調(diào)用。

? ? 3.3.3 sem_getvalue

? ? 函數(shù)原型:

? ??int sem_getvalue(sem_t *sem, int *sval)

? ? sem_getvalue函數(shù)將參數(shù)sem指向的信號量的當前值存儲到參數(shù)sval指向的整形變量中。

? ? 如果,當前有一個或多個進程或線程正在因為調(diào)用sem_wait函數(shù)而堵塞中,那么POSIX標準規(guī)定可以返回兩種數(shù)據(jù)到sval中,一種是0,另外一種是絕對值等于等待進程或線程的數(shù)量的和的負數(shù)。

? ? 返回值:成功返回0,失敗返回-1。

? ? 注意:在實際的應(yīng)用開發(fā)中很少使用此函數(shù),作為程序開發(fā)人員也應(yīng)該盡量避免使用此函數(shù),因為當獲取到value值后,信號量的值可能已經(jīng)改變了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容