@[toc]
訊號量(訊號燈)本質上是乙個計數器,用於協調多個程序(包括但不限於父子程序)對共享資料物件的讀/寫。它不以傳送資料為目的,主要是用來保護共享資源(訊號量、訊息佇列、socket連線等),保證共享資源在乙個時刻只有乙個程序獨享。
訊號量是乙個特殊的變數,只允許程序對它進行等待訊號和傳送訊號操作。最簡單的訊號量是取值0和1的二元訊號量,這是訊號量最常見的形式。
通用訊號量(可以取多個正整數值)和訊號量集方面的知識比較複雜,應用場景也比較少。
本文只介紹二元訊號量。
linux中提供了一組函式用於操作訊號量,程式中需要包含以下標頭檔案:
#include #include #include
semget函式用來獲取或建立訊號量,它的原型如下:
int semget(key_t key, int nsems, int sem***);
1)引數key是訊號量的鍵值,typedef unsigned int key_t,是訊號量在系統中的編號,不同訊號量的編號不能相同,這一點由程式設計師保證。key用十六進製制表示比較好。
2)引數nsems是建立訊號量集中訊號量的個數,該引數只在建立訊號量集時有效,這裡固定填1。
3)引數sem_flags是一組標誌,如果希望訊號量不存在時建立乙個新的訊號量,可以和值ipc_creat做按位或操作。如果沒有設定ipc_creat標誌並且訊號量不存在,就會返錯誤(errno的值為2,no such file or directory)。
4)如果semget函式成功,返回訊號量集的標識;失敗返回-1,錯誤原因存於error中。
示例:1)獲取鍵值為0x5000的訊號量,如果該訊號量不存在,就建立它,**如下:
int semid=semget(0x5000,1,0640|ipc_creat);
2)獲取鍵值為0x5000的訊號量,如果該訊號量不存在,返回-1,errno的值被設定為2,**如下:
int semid= semget(0x5000,1,0640);
該函式用來控制訊號量(常用於設定訊號量的初始值和銷毀訊號量),它的原型如下:
int semctl(int semid, int sem_num, int command, ...);
1)引數semid是由semget函式返回的訊號量標識。
2)引數sem_num是訊號量集陣列上的下標,表示某乙個訊號量,填0。
3)引數cmd是對訊號量操作的命令種類,常用的有以下兩個:
ipc_rmid:銷毀訊號量,不需要第四個引數;
setval:初始化訊號量的值(訊號量成功建立後,需要設定初始值),這個值由第四個引數決定。第四引數是乙個自定義的共同體,如下:
// 用於訊號燈操作的共同體。
union semun
;
4)如果semctl函式呼叫失敗返回-1;如果成功,返回值比較複雜,暫時不關心它。
示例:1)銷毀訊號量。
semctl(semid,0,ipc_rmid);
2)初始化訊號量的值為1,訊號量可用。
union semun sem_union;
sem_union.val = 1;
semctl(semid,0,setval,sem_union);
該函式有兩個功能:1)等待訊號量的值變為1,如果等待成功,立即把訊號量的值置為0,這個過程也稱之為等待鎖;2)把訊號量的值置為1,這個過程也稱之為釋放鎖。
int semop(int semid, struct sembuf *sops, unsigned nsops);
1)引數semid是由semget函式返回的訊號量標識。
2)引數nsops是操作訊號量的個數,即sops結構變數的個數,設定它的為1(只對乙個訊號量的操作)。
3)引數sops是乙個結構體,如下:
struct sembuf
;
示例:1)等待訊號量的值變為1,如果等待成功,立即把訊號量的值置為0;
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = -1;
sem_b.sem_*** = sem_undo;
semop(sem_id, &sem_b, 1);
2)把訊號量的值置為1。
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1;
sem_b.sem_*** = sem_undo;
semop(sem_id, &sem_b, 1);
為了便於理解,我把訊號量的操作封裝成csem類,稱之為訊號燈,類似互斥鎖,包括初始化訊號燈、等待訊號燈、掛出訊號燈和銷毀訊號燈。
/*
* 程式名:book259.cpp,此程式用於演示訊號量的使用方法。
*/#include #include #include #include #include #include class csem
; int sem_id; // 訊號燈描述符。
public:
bool init(key_t key); // 如果訊號燈已存在,獲取訊號燈;如果訊號燈不存在,則建立訊號燈並初始化。
bool wait(); // 等待訊號燈掛出。
bool post(); // 掛出訊號燈。
bool destroy(); // 銷毀訊號燈。
};int main(int argc, char *ar**)
printf("sem.init okn");
// 等待信訊號掛出,等待成功後,將持有鎖。
if (sem.wait()==false)
printf("sem.wait okn");
sleep(50); // 在sleep的過程中,執行其它的book259程式將等待鎖。
// 掛出訊號燈,釋放鎖。
if (sem.post()==false)
printf("sem.post okn");
// 銷毀訊號燈。
// if (sem.destroy()==false)
// printf("sem.destroy okn");
}bool csem::init(key_t key)
// 訊號燈建立成功後,還需要把它初始化成可用的狀態。
union semun sem_union;
sem_union.val = 1;
if (semctl(sem_id,0,setval,sem_union) < 0)
}else
} return true;
}bool csem::destroy()
return true;
}bool csem::wait()
return true;
}bool csem::post()
return true;
}
第一步:執行book259程式,它會建立鍵值為5000的訊號燈,並持有鎖,如下:
第二步:立即再執行乙個book259程式,它會獲取鍵值為5000的訊號燈,並等待鎖,如下:
第三步,當第一次執行的book259程式sleep完50秒之後,釋放鎖,第二個執行book259的程式將獲得鎖;
第四步,可以啟動更多的book259程式,它們將排隊等待鎖。
用ipcs -s 可以檢視系統的訊號量,內容有鍵值(key),訊號量編號(semid),建立者(owner),許可權(perms),訊號量數(nsems)。
用ipcrm -sem 訊號量編號,可以手工刪除訊號量,如下:
C語言筆記 訊號量sem t
includeint sem init sem t sem,int pshared,unsigned int value sem init 初始化乙個定位在 sem 的匿名信號量。value 引數指定訊號量的初始值。pshared 引數指明訊號量是由程序內線程共享,還是由程序之間共享。如果 psha...
訊號量和互斥量C語言示例理解執行緒同步
2.參考資料 了解執行緒訊號量的基礎知識,對深入理解python的執行緒會大有幫助。當兩個執行緒同時執行時,不可避免同時操作同乙個變數或者檔案等,所以需要有一組機制來確保他們能正確的執行 訊號量和互斥量。訊號量可以分為最簡單的 二進位制訊號量 和更通用的 計數訊號量 訊號量通常用來保護一段 使其每次...
哲學家就餐問題C語言實現(涉及多執行緒 訊號量等)
由dijkstra提出並解決的哲學家進餐問題 the dinning philosophers problem 是典型的同步問題。該問題是描述有五個哲學家共用一張圓桌,分別坐在周圍的五張椅子上,在圓桌上有五個碗和五隻筷子,他們的生活方式是交替地進行思考和進餐。平時,乙個哲學家進行思考,飢餓時便試圖取...