Linux環境程序間通訊(四) 訊號燈
一、訊號燈概述
訊號燈與其他程序間通訊方式不大相同,它主要提供對程序間共享資源訪問控制機制。相當於記憶體中的標誌,程序可以根據它判定是否能夠訪問某些共享資源,同時,程序也可以修改該標誌。除了用於訪問控制外,還可用於程序同步。訊號燈有以下兩種型別:
- 二值訊號燈:最簡單的訊號燈形式,訊號燈的值只能取0或1,類似於互斥鎖。
注:二值訊號燈能夠實現互斥鎖的功能,但兩者的關注內容不同。訊號燈強調共享資源,只要共享資源可用,其他程序同樣可以修改訊號燈的值;互斥鎖更強調程序,佔用資源的程序使用完資源後,必須由程序本身來解鎖。 - 計算訊號燈:訊號燈的值可以取任意非負值(當然受核心本身的約束)。
二、Linux訊號燈
linux對訊號燈的支援狀況與訊息佇列一樣,在red had 8.0發行版本中支援的是系統V的訊號燈。因此,本文將主要介紹系統V訊號燈及其相應API。在沒有宣告的情況下,以下討論中指的都是系統V訊號燈。
注意,通常所說的系統V訊號燈指的是計數訊號燈集。
三、訊號燈與核心
1、系統V訊號燈是隨核心持續的,只有在核心重起或者顯示刪除一個訊號燈集時,該訊號燈集才會真正被刪除。因此係統中記錄訊號燈的資料結構(struct ipc_ids sem_ids)位於核心中,系統中的所有訊號燈都可以在結構sem_ids中找到訪問入口。
2、下圖說明了核心與訊號燈是怎樣建立起聯絡的:
其中:struct ipc_ids sem_ids是核心中記錄訊號燈的全域性資料結構;描述一個具體的訊號燈及其相關資訊。
其中,struct sem結構如下:
1234 | struct sem{ int semval; // current value int sempid // pid of last operation } |
從上圖可以看出,全域性資料結構struct ipc_ids sem_ids可以訪問到struct kern_ipc_perm的第一個成員:struct kern_ipc_perm;而每個struct kern_ipc_perm能夠與具體的訊號燈對應起來是因為在該結構中,有一個key_t型別成員key,而key則唯一確定一個訊號燈集;同時,結構struct kern_ipc_perm的最後一個成員sem_nsems確定了該訊號燈在訊號燈集中的順序,這樣核心就能夠記錄每個訊號燈的資訊了。kern_ipc_perm結構參見《Linux環境程序間通訊(三):訊息佇列》。struct sem_array見附錄1。
四、操作訊號燈
對訊息佇列的操作無非有下面三種類型:
1、 開啟或建立訊號燈
與訊息佇列的建立及開啟基本相同,不再詳述。
2、 訊號燈值操作
linux可以增加或減小訊號燈的值,相應於對共享資源的釋放和佔有。具體參見後面的semop系統呼叫。
3、 獲得或設定訊號燈屬性:
系統中的每一個訊號燈集都對應一個struct sem_array結構,該結構記錄了訊號燈集的各種資訊,存在於系統空間。為了設定、獲得該訊號燈集的各種資訊及屬性,在使用者空間有一個重要的聯合結構與之對應,即union semun。
聯合semun資料結構各成員意義參見附錄2
訊號燈API
1、檔名到鍵值
123 | #include < sys /types.h> #include < sys /ipc.h> key_t ftok (char*pathname, char proj); |
它返回與路徑pathname相對應的一個鍵值,具體用法請參考《Linux環境程序間通訊(三):訊息佇列》。
2、 linux特有的ipc()呼叫:
int ipc(unsigned int call, int first, int second, int third, void *ptr, long fifth);
引數call取不同值時,對應訊號燈的三個系統呼叫:
當call為SEMOP時,對應int semop(int semid, struct sembuf *sops, unsigned nsops)呼叫;
當call為SEMGET時,對應int semget(key_t key, int nsems, int semflg)呼叫;
當call為SEMCTL時,對應int semctl(int semid,int semnum,int cmd,union semun arg)呼叫;
這些呼叫將在後面闡述。
注:本人不主張採用系統呼叫ipc(),而更傾向於採用系統V或者POSIX程序間通訊API。原因已在Linux環境程序間通訊(三):訊息佇列中給出。
3、系統V訊號燈API
系統V訊息佇列API只有三個,使用時需要包括幾個標頭檔案:
123 | #include < sys /types.h> #include < sys /ipc.h> #include < sys /sem.h> |
1)int semget(key_t key, int nsems, int semflg)
引數key是一個鍵值,由ftok獲得,唯一標識一個訊號燈集,用法與msgget()中的key相同;引數nsems指定開啟或者新建立的訊號燈集中將包含訊號燈的數目;semflg引數是一些標誌位。引數key和semflg的取值,以及何時開啟已有訊號燈集或者建立一個新的訊號燈集與msgget()中的對應部分相同,不再祥述。
該呼叫返回與健值key相對應的訊號燈集描述字。
呼叫返回:成功返回訊號燈集描述字,否則返回-1。
注:如果key所代表的訊號燈已經存在,且semget指定了IPC_CREAT|IPC_EXCL標誌,那麼即使引數nsems與原來訊號燈的數目不等,返回的也是EEXIST錯誤;如果semget只指定了IPC_CREAT標誌,那麼引數nsems必須與原來的值一致,在後面程式例項中還要進一步說明。
2)int semop(int semid, struct sembuf *sops, unsigned nsops);
semid是訊號燈集ID,sops指向陣列的每一個sembuf結構都刻畫一個在特定訊號燈上的操作。nsops為sops指向陣列的大小。
sembuf結構如下:
12345 | struct sembuf { unsigned short sem_num; /* semaphore index in array */ short sem_op; /* semaphore operation */ short sem_flg; /* operation flags */ }; |
sem_num對應訊號集中的訊號燈,0對應第一個訊號燈。sem_flg可取IPC_NOWAIT以及SEM_UNDO兩個標誌。如果設定了SEM_UNDO標誌,那麼在程序結束時,相應的操作將被取消,這是比較重要的一個標誌位。如果設定了該標誌位,那麼在程序沒有釋放共享資源就退出時,核心將代為釋放。如果為一個訊號燈設定了該標誌,核心都要分配一個sem_undo結構來記錄它,為的是確保以後資源能夠安全釋放。事實上,如果程序退出了,那麼它所佔用就釋放了,但訊號燈值卻沒有改變,此時,訊號燈值反映的已經不是資源佔有的實際情況,在這種情況下,問題的解決就靠核心來完成。這有點像殭屍程序,程序雖然退出了,資源也都釋放了,但核心程序表中仍然有它的記錄,此時就需要父程序呼叫waitpid來解決問題了。
sem_op的值大於0,等於0以及小於0確定了對sem_num指定的訊號燈進行的三種操作。具體請參考linux相應手冊頁。
這裡需要強調的是semop同時操作多個訊號燈,在實際應用中,對應多種資源的申請或釋放。semop保證操作的原子性,這一點尤為重要。尤其對於多種資源的申請來說,要麼一次性獲得所有資源,要麼放棄申請,要麼在不佔有任何資源情況下繼續等待,這樣,一方面避免了資源的浪費;另一方面,避免了程序之間由於申請共享資源造成死鎖。
也許從實際含義上更好理解這些操作:訊號燈的當前值記錄相應資源目前可用數目;sem_op>0對應相應程序要釋放sem_op數目的共享資源;sem_op=0可以用於對共享資源是否已用完的測試;sem_op<0相當於程序要申請-sem_op個共享資源。再聯想操作的原子性,更不難理解該系統呼叫何時正常返回,何時睡眠等待。
呼叫返回:成功返回0,否則返回-1。
3) int semctl(int semid,int semnum,int cmd,union semun arg)
該系統呼叫實現對訊號燈的各種控制操作,引數semid指定訊號燈集,引數cmd指定具體的操作型別;引數semnum指定對哪個訊號燈操作,只對幾個特殊的cmd操作有意義;arg用於設定或返回訊號燈資訊。
該系統呼叫詳細資訊請參見其手冊頁,這裡只給出引數cmd所能指定的操作。
IPC_STAT | 獲取訊號燈資訊,資訊由arg.buf返回; |
---|---|
IPC_SET | 設定訊號燈資訊,待設定資訊儲存在arg.buf中(在manpage中給出了可以設定哪些資訊); |
GETALL | 返回所有訊號燈的值,結果儲存在arg.array中,引數sennum被忽略; |
GETNCNT | 返回等待semnum所代表訊號燈的值增加的程序數,相當於目前有多少程序在等待semnum代表的訊號燈所代表的共享資源; |
GETPID | 返回最後一個對semnum所代表訊號燈執行semop操作的程序ID; |
GETVAL | 返回semnum所代表訊號燈的值; |
GETZCNT | 返回等待semnum所代表訊號燈的值變成0的程序數; |
SETALL | 通過arg.array更新所有訊號燈的值;同時,更新與本訊號集相關的semid_ds結構的sem_ctime成員; |
SETVAL | 設定semnum所代表訊號燈的值為arg.val; |
呼叫返回:呼叫失敗返回-1,成功返回與cmd相關:
Cmd | return value |
---|---|
GETNCNT | Semncnt |
GETPID | Sempid |
GETVAL | Semval |
GETZCNT | Semzcnt |
五、訊號燈的限制
1、 一次系統呼叫semop可同時操作的訊號燈數目SEMOPM,semop中的引數nsops如果超過了這個數目,將返回E2BIG錯誤。SEMOPM的大小特定與系統,redhat 8.0為32。
2、 訊號燈的最大數目:SEMVMX,當設定訊號燈值超過這個限制時,會返回ERANGE錯誤。在redhat 8.0中該值為32767。
3、 系統範圍內訊號燈集的最大數目SEMMNI以及系統範圍內訊號燈的最大數目SEMMNS。超過這兩個限制將返回ENOSPC錯誤。redhat 8.0中該值為32000。
4、 每個訊號燈集中的最大訊號燈數目SEMMSL,redhat 8.0中為250。 SEMOPM以及SEMVMX是使用semop呼叫時應該注意的;SEMMNI以及SEMMNS是呼叫semget時應該注意的。SEMVMX同時也是semctl呼叫應該注意的。
六、競爭問題
第一個建立訊號燈的程序同時也初始化訊號燈,這樣,系統呼叫semget包含了兩個步驟:建立訊號燈;初始化訊號燈。由此可能導致一種競爭狀態:第一個建立訊號燈的程序在初始化訊號燈時,第二個程序又呼叫semget,並且發現訊號燈已經存在,此時,第二個程序必須具有判斷是否有程序正在對訊號燈進行初始化的能力。在參考文獻[1]中,給出了繞過這種競爭狀態的方法:當semget建立一個新的訊號燈時,訊號燈結構semid_ds的sem_otime成員初始化後的值為0。因此,第二個程序在成功呼叫semget後,可再次以IPC_STAT命令呼叫semctl,等待sem_otime變為非0值,此時可判斷該訊號燈已經初始化完畢。下圖描述了競爭狀態產生及解決方法:
實際上,這種解決方法也是基於這樣一個假定:第一個建立訊號燈的程序必須呼叫semop,這樣sem_otime才能變為非零值。另外,因為第一個程序可能不呼叫semop,或者semop操作需要很長時間,第二個程序可能無限期等待下去,或者等待很長時間。
七、訊號燈應用例項
本例項有兩個目的:1、獲取各種訊號燈資訊;2、利用訊號燈實現共享資源的申請和釋放。並在程式中給出了詳細註釋。
123456789101112131415161718192021222324252627282930313233343536373839相關推薦Linux環境程序間通訊(四) 訊號燈(轉)轉自http://www.ibm.com/developerworks/cn/linux/l-ipc/part4/, 作者:鄭彥興訊號燈與其他程序間通訊方式不大相同,它主要提供對程序間共享資源訪問控制機制。相當於記憶體中的標誌,程序可以根據它判定是否能夠訪問某些共享資源,同時,程序也可以修改該標誌。除了用於訪 Linux環境程序間通訊(四) 訊號燈一、訊號燈概述訊號燈與其他程序間通訊方式不大相同,它主要提供對程序間共享資源訪問控制機制。相當於記憶體中的標誌,程序可以根據它判定是否能夠訪問某些共享資源,同時,程序也可以修改該標誌。除了用於訪問控制外,還可用於程序同步。訊號燈有以下兩種型別:二值訊號燈:最簡單的訊號燈形式, Linux環境程序間通訊(五): 共享記憶體(下)(轉)轉自http://www.ibm.com/developerworks/cn/linux/l-ipc/part5/index2.html, 作者:鄭彥興系統呼叫mmap()通過對映一個普通檔案實現共享記憶體。系統V則是通過對映特殊檔案系統shm中的檔案實現程序間的共享記憶體通訊。也就是說,每個共享記憶體區域對 Linux環境程序間通訊(五): 共享記憶體(上)(轉)轉自http://www.ibm.com/developerworks/cn/linux/l-ipc/part5/index1.html, 作者:鄭彥興採用共享記憶體通訊的一個顯而易見的好處是效率高,因為程序可以直接讀寫記憶體,而不需要任何資料的拷貝。對於像管道和訊息佇列等通訊方式,則需要在內 核和使用者空間 Linux環境程序間通訊(二): 訊號(上)(轉)訊號本質訊號是在軟體層次上對中斷機制的一種模擬,在原理上,一個程序收到一個訊號與處理器收到一箇中斷請求可以說是一樣的。訊號是非同步的,一個程序不必通過任何操作來等待訊號的到達,事實上,程序也不知道訊號到底什麼時候到達。訊號是程序間通訊機制中唯一的非同步通訊機制,可以看作是非同步通知,通知接收訊號的程序有哪些事 Linux 環境程序間通訊(六) 套介面(轉)轉自https://www.ibm.com/developerworks/cn/linux/l-ipc/part6/, 作者:鄭彥興一個套介面可以看作是程序間通訊的端點(endpoint),每個套介面的名字都是唯一的(唯一的含義是不言而喻的),其他程序可以發現、連線並且 與之通訊。通訊域用來說明套介面通訊的協 Linux環境程序間通訊(三) 訊息佇列(轉)轉自http://www.ibm.com/developerworks/cn/linux/l-ipc/part3/, 作者:鄭彥興訊息佇列(也叫做報文佇列)能夠克服早期unix通訊機制的一些缺點。作為早期unix通訊機制之一的訊號能夠傳送的資訊量有限,後來雖然 POSIX 1003.1b在訊號的實時性方面作了 Linux環境程序間通訊(二): 訊號(下)(轉)從訊號傳送到訊號處理函式的執行完畢對於一個完整的訊號生命週期(從訊號傳送到相應的處理函式執行完畢)來說,可以分為三個重要的階段,這三個階段由四個重要事件來刻畫:訊號誕生;訊號在程序中註冊完畢;訊號在程序中的登出完畢;訊號處理函式執行完畢。相鄰兩個事件的時間間隔構成訊號生命週期的一個階段。 下面闡述四個事件的實 Linux環境程序間通訊(一) 管道及有名管道(轉)管道是Linux支援的最初Unix IPC形式之一,具有以下特點:管道是半雙工的,資料只能向一個方向流動;需要雙方通訊時,需要建立起兩個管道;只能用於父子程序或者兄弟程序之間(具有親緣關係的程序);單獨構成一種獨立的檔案系統:管道對於管道兩端的程序而言,就是一個檔案,但它不是普通的檔案,它不屬於某種檔案系統, Linux環境程序間通訊(五) 共享記憶體(下)系統呼叫mmap()通過對映一個普通檔案實現共享記憶體。系統V則是通過對映特殊檔案系統shm中的檔案實現程序間的共享記憶體通訊。也就是說,每個共享記憶體區域對應特殊檔案系統shm中的一個檔案(這是通過shmid_kernel結構聯絡起來的),後面還將闡述。 1、系統V共 Linux 程序間通訊(四)訊號量1 訊號量概述 訊號量和其他IPC不同,並沒有在程序之間傳送資料,訊號量用於多程序在存取共享資源時的同步控制就像交通路口的紅路燈一樣,當訊號量大於0,表示綠燈允許通過,當訊號量等於0,表示紅燈,必須停下來等待綠燈才能通過。 程序間的互斥關係與同步關係存在的根源在於臨界資 Linux程序間通訊(四)訊號量雖然本文是記錄使用訊號量保證程序的同步與互斥的,但是其實也可以看做是程序之間的通訊問題,為了與前面的保持一致,所以還是叫做 Linux程序間通訊了 (強迫症...) 訊號量 基本概念 程序間通訊的方式有管道、訊息佇列、共享記憶體這些都是程序間的資訊通訊,而訊號量可以理解為程序使用的臨界資源的狀態說明,訊 Linux程序間通訊(四)一、什麼是訊號量: 訊號量的本質是一種資料操作鎖,它本身不具有資料交換的功能,而是通過控制其他的通訊資源(檔案,外部裝置)來實現程序間通訊,它本身只是一種外部資源的標識。訊號量在此過程中負責資料操作的互斥、同步等功能。 當請求一個使用訊號量來表示的資源時,程序需要先讀取 Android IPC程序間通訊(四)AIDLAIDL-Android介面定義語言 一· 1.相比於Messenger AIDL可跨程序呼叫方法。 2.支援資料型別: (1) Java 的原生基本型別(int, long, char, boolean, double等) (2)String 和CharSequence (3) Arr 【Linux】程序間通訊(IPC)之訊號量詳解與測試用例學習環境centos6.5 Linux核心2.6 程序間通訊概述 1. 程序通訊機制 一般情況下,系統中執行著大量的程序,而每個程序之間並不是相互獨立的,有些程序之間經常需要互相傳遞訊息。但是每個程序在系統中都有自己的地址空間,作業系統通過頁表 【Linux】程序間通訊(IPC)之訊息佇列詳解及測試用例學習環境 Centos6.5 Linux 核心 2.6 什麼是訊息佇列? 訊息佇列是SystemV版本中三種程序通訊機制之一,另外兩種是訊號量和共享儲存段。訊息佇列提供了程序間傳送資料塊的方法,而且每個資料塊都有一個型別標識。訊息佇列是基於訊息的,而管 【Linux】程序間通訊(IPC)之共享記憶體詳解與測試用例學習環境centos6.5 Linux核心2.6 什麼是共享記憶體 共享記憶體允許兩個或更多程序訪問同一塊記憶體。當一個程序改變了這塊記憶體中的內容的的時候,其他程序都會察覺到這個更改。 效率: 因為所有程序共享同一塊記憶體,共享記憶體在各種程序 Linux程序間通訊(IPC)方式總結程序間通訊概述 程序通訊的目的 資料傳輸 一個程序需要將它的資料傳送給另一個程序,傳送的資料量在一個位元組到幾M位元組之間 共享資料 多個程序想要操作共享資料,一個程序對共享資料 通知事件 一個程序需要向另一個或一組程序傳送訊息,通知它(它們) Linux:程序間通訊(匿名管道命名管道)(共享記憶體,訊息佇列,訊號量)目錄 程序間通訊的介紹 管道 匿名管道 原理: 程式碼實現 匿名管道特性 實現管道符 | 命名管道 命名管道特性 程式碼實現 管道讀寫規則 作業系統中ipc的相關命令 共享記憶體(重點) 生命週期: 程式碼實現 程式碼實現獲 Linux 程序間通訊(五)IPC的特性1.識別符號和鍵 每個核心中的IPC結構(訊息佇列、訊號量或共享儲存段)都用一個非負整數的識別符號 (identifier)加以引用。 例如,要向一個訊息佇列傳送訊息或者從一個訊息佇列取訊息,只需要知道其佇列識別符號。 當一個IPC結構被建立,然後又被刪除時,與這種結構 |