Linux環境程序間通訊(四) 訊號燈

2021-04-14 21:08:27 字數 4674 閱讀 1432

一、訊號燈概述

訊號燈與其他程序間通訊方式不大相同,它主要提供對程序間共享資源訪問控制機制。相當於記憶體中的標誌,程序可以根據它判定是否能夠訪問某些共享資源,同時,程序也可以修改該標誌。除了用於訪問控制外,還可用於程序同步。訊號燈有以下兩種型別:

二、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結構如下:

struct sem
從上圖可以看出,全域性資料結構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、檔名到鍵值

#include #include key_t ftok (char*pathname, char proj);
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 sem***)呼叫;

當call為semctl時,對應int semctl(int semid,int semnum,int cmd,union semun arg)呼叫;

這些呼叫將在後面闡述。

注:本人不主張採用系統呼叫ipc(),而更傾向於採用系統v或者posix程序間通訊api。原因已在linux環境程序間通訊(三):訊息佇列中給出。

3、系統v訊號燈api

系統v訊息佇列api只有三個,使用時需要包括幾個標頭檔案:

#include #include #include
1)int semget(key_t key, int nsems, int sem***)

引數key是乙個鍵值,由ftok獲得,唯一標識乙個訊號燈集,用法與msgget()中的key相同;引數nsems指定開啟或者新建立的訊號燈集中將包含訊號燈的數目;sem***引數是一些標誌位。引數key和sem***的取值,以及何時開啟已有訊號燈集或者建立乙個新的訊號燈集與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結構如下:

struct sembuf ;

sem_num對應訊號集中的訊號燈,0對應第乙個訊號燈。sem_***可取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相關:

cmdreturn 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呼叫應該注意的。

Linux 程序間通訊 訊號

訊號是在軟體層次上對中斷機制的一種模擬,在原理上,乙個程序收到乙個訊號與處理器收到乙個中斷請求可以說是一樣的。訊號是非同步的,乙個程序不必通過任何操作來等待訊號的到達,事實上,程序也不知道訊號到底什麼時候到達。訊號是程序間通訊機制中唯一的非同步通訊機制,可以看作是非同步通知,通知接收訊號的程序有哪些...

Linux程序間通訊 訊號

1.什麼是訊號 訊號是linux系統響應某些條件而產生的乙個事件,接收到該訊號的程序會執行相應的操作。2.訊號的產生 1 由硬體產生,如從鍵盤輸入ctrl c可以終止當前程序 2 由其他程序傳送,如可在shell程序下,使用命令 kill 訊號標號 pid,向指定程序傳送訊號。3 異常,程序異常時會...

Linux程序間通訊 訊號

訊號 signals 是unix 類unix以及其他posix相容的作業系統中程序間通訊的一種有限制的方式。它是一種非同步的通知機制,用來提醒程序乙個事件已經發生。當乙個訊號傳送給乙個程序,作業系統中斷了程序正常的控制流程,此時,任何非原子操作都將被中斷。如果程序定義了訊號的處理函式,那麼它將被執行...