Linux 信号量
信號量
- 信號量
- 信號量的定義
- 信號量理論例子
- Linux信號量機制
- 使用信號量
信號量
信號量:用于管理對資源的訪問。
(1) 當我們編寫的程序使用了線程時,不管它是運行在多用戶系統上、多進程系統上,還是運行在多用戶多進程系統上,我們通常會發現,程序中存在著一部分臨 界代碼,我們需要確保只有一個進程 (或一個執行線程) 可以進入這個臨界代碼并擁有對資源獨占式的訪問權。
(2)信號量有著復雜的編程接口,但幸運的是,我們可以很輕松地為自己提供一個更簡單的接口,它足夠應付大多數信號量編程的問題。
(3)為了防止出現因多個程序同時訪問-一個共享資源而引發的問題,我們需要有一種方法,它可以通過生成并使用令牌來授權,在任一時刻只能有一個執行線程訪問代碼的臨界區域。
(4)要想編寫通用的代碼,以確保程序對某個特定的資源具有獨占式的訪問權是非常困難的。雖然有一個名為Dekker算法的解決方法,但這個算法依賴于“忙等待”或“自旋鎖”。也就是說,一個進程要持續不斷地運行以等待某個內存位置被改變。在像Linux這樣的多任務環境中,人們并不愿意使用這種浪費CPU資源的處理方法。但如果硬件支持獨占式訪問(一般是通過特定的CPU指令的形式),那么情況就變得簡單多了。一個硬件支持的例子就是,用一條指令以原子方式訪問并增加寄存器的值,在這個讀取/增加/寫入操作執行的過程中不會有其他指令(甚至一個中斷)發生。
(5)一種可能的解決方法是,使用帶O_ EXCL標志的open函數來創建鎖文件,它提供了原子化的文件創建方法。它允許一個進程通過獲取一個令牌(即新創建的文件)來取得成功。這個方法比較適合于處理簡單的問題,但對于更復雜的例子,它就顯得比較雜亂且缺乏效率。
(6)荷蘭計算機科學家Edsger Dijkstra提出的信號量概念是在并發編程領域邁出的重要一步。 信號量是一個特殊的變量,它只取正整數值,并且程序對其訪問都是原子操作。
信號量的一個更正式的定義是:它是一個特殊變量,只允許對它進行等待(wait) 和發送信號(signal)這兩種操作。因為在Linux編程中,“等待”和“發送信號”都已具有特殊的含義,所以我們將用原先定義的符號來表示這兩種操作。
- P (信號量變量):用于等待。
- V (信號量變量):用于發送信號。
這兩個字母分別來自于荷蘭語單詞passeren(傳遞,就好像位于進入臨界區域之前的檢查點)和vrijgeven (給予或釋放,就好像放棄對臨界區域的控制權)。在與信號量關聯的內容中,你可能還會看到術語“開”(up)和“關”(down),它們取自開、關信號標志的用法。
信號量的定義
最簡單的信號量是只能取值0和1的變量,即二進制信號量。這也是信號量最常見的一一種形式??梢匀《鄠€正整數值的信號量被稱為通用信號量。
PV操作的定義非常簡單。假設有一個信號量變量sv,則這兩個操作的定義如圖:
還可以這樣看信號量:當臨界區域可用時,信號量變量sv的值是true,然后P(sv)操作將它減1使它變為false以表示臨界區域正在被使用;當進程離開臨界區域時,使用V(sv) 操作將它加1,使臨界區域再次變為可用。注意,只用一個普通變量進行類似的加減法是不行的,因為在C、C++、C#或幾乎任何一個傳統的編程語言中,都沒有一個原子操作可以滿足檢測變量是否為true,如果是再將該變量設置為false的需要。這也是信號量操作如此特殊的原因。
信號量理論例子
我們用一個簡單的理論性的例子來說明其工作原理。假設有兩個進程proc1和proc2,這兩個進程都需要在其執行過程中的某–時刻對–個數據庫進行獨占式的訪問。我們定義一一個二進制信號量sv,該變量的初始值為1,兩個進程都可以訪問它。要想對代碼中的臨界區域進行訪問,這兩個進程都需要執行相同的處理步驟,事實上,這兩個進程可以只是同一個程序的兩個不同執行實例。兩個進程共享信號量變量sv。一旦其中一個進程執行了P(sv)操作,它將得到信號量,并可以進入臨界區域。而第二個進程將被阻止進入臨界區域,因為當它試圖執行P(sv)操作時,它會被掛起以等待第一一個進程離開臨界區域并執行V(sv)操作釋放信號量。
需要的偽代碼對兩個進程都是相同的,如下所示:
這段代碼相當簡單,這是因為PV操作的功能非常強大。如圖顯示了Pv操作是如何把守代碼中的臨界區域的。
Linux信號量機制
. 頭文件
#include <sys/sem.h>#include <sys/types.h>#include <sys/ipc.h>#include <semaphore.h>- int semget(key_t key, int nsems, int semflg);
- *int semop( int semid, struct sembuf sops, unsigned nsops);
- int semctl( int semid, int semnum, int cmd, …);
- int sem_init(sem_t *sem, int pshared, unsigned int value);
- int sem_wait(sem_t *sem);
- int sem_post(sem_t *sem);
- int sem_destroy(sem_t *sem);
使用信號量
兩線程完成輸入
#include <stdio.h> #include <stdlib.h> #include <assert.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include <semaphore.h> #include <fcntl.h>char buff[128] = {0};sem_t sem1;sem_t sem2;void* PthreadFun( void *arg){int fd = open("a.txt", O_RDWR | O_CREAT, 0664);assert(fd != -1);//函數線程完成將用戶輸入的數據存儲到文件中while(1){sem_wait(&sem2);if(strncmp(buff, "end", 3) == 0){break;}write(fd, buff, strlen(buff));memset(buff, 0, 128);sem_post(&sem1);}sem_destroy(&sem1);sem_destroy(&sem2);}int main(){sem_init(&sem1, 0, 1);sem_init(&sem2, 0, 0);pthread_t id;int res = pthread_create(&id, NULL, PthreadFun, NULL);assert(res == 0);//主線程完成獲取用戶數據的數據,并存儲在全局數組 buff 中while(1){sem_wait(&sem1);printf("please input data: ");fflush(stdout);fgets(buff, 128, stdin);buff[strlen(buff) - 1] = 0;sem_post(&sem2);if(strncmp(buff, "end", 3) == 0){break;}}pthread_exit(NULL);}總結
- 上一篇: linux c post上传文件,Lin
- 下一篇: EEGLAB的下载与安装