日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

站在巨人的肩膀上——Linux信号量操作

發布時間:2023/12/31 linux 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 站在巨人的肩膀上——Linux信号量操作 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

感謝那些讓我度過此學海的無名勇士


信號量簡介:

在對于臨界區資源管理的過程中,多個程序同時訪問一個共享資源經常容易引發一系列問題:如死鎖,結果不唯一等等,

在1965年,由荷蘭科學家E.W.Dijkstra提出了一種新的進程同步工具,信號量及其PV操作。

對于信號量的定義是這樣的:

??????????????? 讓多個進程通過特殊變量展開交互,一個進程在某一個關鍵點上被迫停止執行直至接收到對應的特殊變量值,通過這一措施,任何復雜的進程交互要求均可得到滿足,這種特殊的變量就是信號量。

信號量的種類分為以下2種:

一般信號量:

?????????????? 設s為一個記錄型數據結構,其中value為整型變量,系統初始化時為其賦值,PV操作的原語描述如下:

?????????????? P(s):將信號量value值減1,若結果小于0,則執行P操作的進程被阻塞,若結果大于等于0,則執行P操作的進程將繼續執行。

?????????????? V(s):將信號量的值加1,若結果不大于0,則執行V操作的進程從信號量s有關的list所知隊列中釋放一個進程,使其轉化為就緒態,自己則繼續執行,若結果大于0,則執行V操作的進程繼續執行。

二值信號量:

????????????? 設s為一個記錄型數據結構,其中分量value僅能取值0或1,二值信號量的PV操作的原語描述和一般信號量相同,雖然二值信號量僅能取值0或1,但可以證明他有著與其他信號量相同的表達能力

?????????? ?

當然以上都是創造者所給出的定義,對于實現方式在不同的平臺下接口會有不同,以下的實現部分均是建立在Linux平臺下使用C語言編碼實現,主要介紹的也是對應的C接口

上面那些定義總結起來可以這樣說:通過使用信號量生成令牌來授權,在任一時刻只能有一個執行線程訪問代碼的臨界區域。臨界區域是指執行數據更新的代碼需要獨占式地執行。而信號量就可以提供這樣的一種訪問機制,讓一個臨界區同一時間只有一個線程在訪問它,也就是說信號量是用來調協進程對共享資源的訪問的一種手段。


函數操作:

對于與信號量操作有關的接口,Linux下主要提供了以下幾個函數,值得注意的是,在Linux下的C接口中,這些函數的操作對象都是信號量值組,也就是一個信號量值的鏈表


int?semget(key_t?key,?int?num_sems,?int?sem_flags);

該函數的作用是創建一個新信號量或取得一個已有信號量。

第一個參數key是整數值(唯一非零),就是Linux線程操作中經常用到的鍵值,可以通過ftok函數得到,不相關的進程可以通過它訪問一個信號量,它代表程序可能要使用的某個資源,程序對所有信號量的訪問都是間接的,程序先通過調用semget函數并提供一個鍵,再由系統生成一個相應的信號標識符(semget函數的返回值),只有semget函數才直接使用信號量鍵,所有其他的信號量函數使用由semget函數返回的信號量標識符。如果多個程序使用相同的key值,key將負責協調工作。

第二個參數num_sems指定需要的信號量數目,它的值幾乎總是1。

第三個參數sem_flags是一組標志,當想要當信號量不存在時創建一個新的信號量,可以和值IPC_CREAT做按位或操作。設置了IPC_CREAT標志后,即使給出的鍵是一個已有信號量的鍵,也不會產生錯誤。而IPC_CREAT | IPC_EXCL則可以創建一個新的,唯一的信號量,如果信號量已存在,返回一個錯誤。

semget函數成功返回一個相應信號標識符(非零),失敗返回-1



int semop(int sem_id, struct sembuf *sops, size_t nsops);

該函數的作用是改變信號量的值,其實就是為了信號量的PV操作而準備的,這個函數可以講的地方比較多,下面會詳細介紹:

函數的第一個參數 semid 為信號量集的標識符;

第2個參數 sops 指向進行操作的結構體數組的首地址,在 semop 的第二個參數 sops 指向的結構體數組中,每個 sembuf 結構體對應一個特定信號的操作。因此對信號量進行操作必須熟悉該數據結構,該結構定義在 linux/sem.h,如下所示:

struct sembuf{ unsigned short sem_num; //信號在信號集中的索引,0代表第一個信號,1代表第二個信號 short sem_op; //操作類型 short sem_flg; //操作標志 };
struct sembuf{
unsigned short sem_num; //信號在信號集中的索引,0代表第一個信號,1代表第二個信號
short sem_op; //操作類型
short sem_flg; //操作標志
};

對于該結構中各個成員都具有特殊的含義,具體含義的介紹如下:


sem_op 參數:

sem_op > 0
信號加上 sem_op 的值,表示進程釋放控制的資源;

sem_op = 0 如果sem_flg沒有設置IPC_NOWAIT,則調用進程進入睡眠狀態,直到信號量的值為0;否則進程不會睡眠,直接返回 EAGAIN

sem_op < 0 信號加上 sem_op 的值。若沒有設置 IPC_NOWAIT ,則調用進程阻
塞,直到資源可用;否則進程直接返回EAGAIN

sem_flg 參數:


該參數可設置為 IPC_NOWAIT 或 SEM_UNDO 兩種狀態。只有將 sem_flg 指定為 SEM_UNDO 標志后,semadj (所指定信號量針對調用進程的調整值)才會更新。 此外,如果此操作指定SEM_UNDO,系統更新過程中會撤消此信號燈的計數(semadj)。此操作可以隨時進行---它永遠不會強制等待的過程。調用進程必須有改變信號量集的權限。
sem_flg公認的標志是 IPC_NOWAIT 和 SEM_UNDO。如果操作指定SEM_UNDO,當該進程終止時它將會自動撤消。

第3個參數 nsops 指出將要進行操作的信號的個數。semop 函數調用成功返回 0,失敗返回 -1。

該函數所做的對于信號量的操作都是原子操作,即整個行為是一個整體,是不可打斷的。所有操作是否可以立即執行取決于在個人sem_flg領域的IPC_NOWAIT標志的存在。


int semctl(int sem_id, int sem_num, int command, ...);

函數的第一個參數 semid 為信號量集的標識符;

函數的第二個參數sem_num則是表示即將要進行操作的信號量的編號,即信號量集合的索引值,其中第一個信號量的索引值為0。

函數的第3個參數command代表將要在集合上執行的命令,其取值含義如下,通常用特定的宏代替:

IPC_STAT:獲取某個信號量集合的semid_ds結構,并將其儲存在semun聯合體的buf參數所指的地址之中

IPC_SET:設置某個集合的semid_ds結構的ipc_perm成員的值,該命令所取的值是從semun聯合體的buf參數中取到的

IPC_RMID:從內核刪除該信號量集合

GETALL:用于獲取集合中所有信號量的值,整數值存放在無符號短整數的一個數組中,該數組有聯合體的array成員所指定

GETNCNT:返回當前正在等待資源的進程的數目

GETPID:返回最后一次執行PV操作(semop函數調用)的進程的PID

GETVAL:返回集合中某個信號量的值

GETZCNT:返回正在等待資源利用率達到百分之百的進程的數目

SETALL:把集合中所有信號量的值,設置為聯合體的array成員所包含的對應值

SETVAL:將集合中單個信號量的值設置為聯合體的val成員的值

其中semun聯合體的結構如下:

union semun{ int val; struct semid_ds *buf; unsigned short *array; struct seminfo *__buf; };

對于該函數,只有當command取某些特定的值的時候,才會使用到第4個參數,第4個參數它通常是一個union semum結構,定義如下:

union semun{ int val; struct semid_ds *buf; unsigned short *arry; }; 對于第4個參數arg,

當執行SETVAL命令時用到這個成員,他用于指定要把信號量設置成什么值,涉及成員:val

在命令IPC_STAT/IPC_SET中使用,它代表內核中所使用內部信號量數據結構的一個復制 ,涉及成員:buf

在命令GETALL/SETALL命令中使用時,他代表指向整數值一個數組的指針,在設置或獲取集合中所有信號量的值的過程中,將會用到該數組,涉及成員:array

剩下的還有一些用法都將在系統內核中的信號量代碼使用,應用程序開發中使用很少,這里也就不介紹了。


實現樣例:

這里列舉一個別人的樣例,主要是為了展示信號量控制進程的操作代碼如下:

#include<iostream> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/sem.h> using namespace std;union semun {int val;struct semid_ds *buf;unsigned short *arry; };static int sem_id = 0; static int set_semvalue(); static void del_semvalue(); static int semaphore_p(); static int semaphore_v(); int main(int argc, char *argv[]) { char message = 'S'; int i = 0; //創建信號量 sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT); if(argc > 1) { //程序第一次被調用,初始化信號量 if(!set_semvalue()) { fprintf(stderr, "Failed to initialize semaphore\n"); exit(EXIT_FAILURE); } //設置要輸出到屏幕中的信息,即其參數的第一個字符 message = argv[1][0]; sleep(2); } cout<<argc<<message<<endl;for(i = 0; i < 10; ++i) { //進入臨界區 if(!semaphore_p()) exit(EXIT_FAILURE); //向屏幕中輸出數據 printf("進入%c", message); //清理緩沖區,然后休眠隨機時間 fflush(stdout); sleep(rand() % 3); //離開臨界區前再一次向屏幕輸出數據 printf("離開%c\n", message); fflush(stdout); //離開臨界區,休眠隨機時間后繼續循環 if(!semaphore_v()) exit(EXIT_FAILURE); sleep(rand() % 2); } sleep(10); printf("\n%d - finished\n", getpid()); if(argc > 1) { //如果程序是第一次被調用,則在退出前刪除信號量 sleep(3); del_semvalue(); } exit(EXIT_SUCCESS); } static int set_semvalue() { //用于初始化信號量,在使用信號量前必須這樣做 union semun sem_union; sem_union.val = 1; if(semctl(sem_id, 0, SETVAL, sem_union) == -1) return 0; return 1; } static void del_semvalue() { //刪除信號量 union semun sem_union; if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1) fprintf(stderr, "Failed to delete semaphore\n"); elsefprintf(stdout, "已經刪除信號量\n"); } static int semaphore_p() { //對信號量做減1操作,即等待P(sv) struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_op = -1;//P() sem_b.sem_flg = SEM_UNDO; if(semop(sem_id, &sem_b, 1) == -1) { fprintf(stderr, "semaphore_p failed\n"); return 0; } return 1; } static int semaphore_v() { //這是一個釋放操作,它使信號量變為可用,即發送信號V(sv) struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_op = 1;//V() sem_b.sem_flg = SEM_UNDO; if(semop(sem_id, &sem_b, 1) == -1) { fprintf(stderr, "semaphore_v failed\n"); return 0; } return 1; } 編譯及運行指令為:

g++ -o 可執行文件名 代碼文件名 //編譯 ./可執行文件名 1 2 & ./可執行文件名 //運行 運行效果圖如下:


總結

以上是生活随笔為你收集整理的站在巨人的肩膀上——Linux信号量操作的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。