Linux 信号量
信號(hào)量
- 信號(hào)量
- 信號(hào)量的定義
- 信號(hào)量理論例子
- Linux信號(hào)量機(jī)制
- 使用信號(hào)量
信號(hào)量
信號(hào)量:用于管理對(duì)資源的訪問。
(1) 當(dāng)我們編寫的程序使用了線程時(shí),不管它是運(yùn)行在多用戶系統(tǒng)上、多進(jìn)程系統(tǒng)上,還是運(yùn)行在多用戶多進(jìn)程系統(tǒng)上,我們通常會(huì)發(fā)現(xiàn),程序中存在著一部分臨 界代碼,我們需要確保只有一個(gè)進(jìn)程 (或一個(gè)執(zhí)行線程) 可以進(jìn)入這個(gè)臨界代碼并擁有對(duì)資源獨(dú)占式的訪問權(quán)。
(2)信號(hào)量有著復(fù)雜的編程接口,但幸運(yùn)的是,我們可以很輕松地為自己提供一個(gè)更簡(jiǎn)單的接口,它足夠應(yīng)付大多數(shù)信號(hào)量編程的問題。
(3)為了防止出現(xiàn)因多個(gè)程序同時(shí)訪問-一個(gè)共享資源而引發(fā)的問題,我們需要有一種方法,它可以通過生成并使用令牌來授權(quán),在任一時(shí)刻只能有一個(gè)執(zhí)行線程訪問代碼的臨界區(qū)域。
(4)要想編寫通用的代碼,以確保程序?qū)δ硞€(gè)特定的資源具有獨(dú)占式的訪問權(quán)是非常困難的。雖然有一個(gè)名為Dekker算法的解決方法,但這個(gè)算法依賴于“忙等待”或“自旋鎖”。也就是說,一個(gè)進(jìn)程要持續(xù)不斷地運(yùn)行以等待某個(gè)內(nèi)存位置被改變。在像Linux這樣的多任務(wù)環(huán)境中,人們并不愿意使用這種浪費(fèi)CPU資源的處理方法。但如果硬件支持獨(dú)占式訪問(一般是通過特定的CPU指令的形式),那么情況就變得簡(jiǎn)單多了。一個(gè)硬件支持的例子就是,用一條指令以原子方式訪問并增加寄存器的值,在這個(gè)讀取/增加/寫入操作執(zhí)行的過程中不會(huì)有其他指令(甚至一個(gè)中斷)發(fā)生。
(5)一種可能的解決方法是,使用帶O_ EXCL標(biāo)志的open函數(shù)來創(chuàng)建鎖文件,它提供了原子化的文件創(chuàng)建方法。它允許一個(gè)進(jìn)程通過獲取一個(gè)令牌(即新創(chuàng)建的文件)來取得成功。這個(gè)方法比較適合于處理簡(jiǎn)單的問題,但對(duì)于更復(fù)雜的例子,它就顯得比較雜亂且缺乏效率。
(6)荷蘭計(jì)算機(jī)科學(xué)家Edsger Dijkstra提出的信號(hào)量概念是在并發(fā)編程領(lǐng)域邁出的重要一步。 信號(hào)量是一個(gè)特殊的變量,它只取正整數(shù)值,并且程序?qū)ζ湓L問都是原子操作。
信號(hào)量的一個(gè)更正式的定義是:它是一個(gè)特殊變量,只允許對(duì)它進(jìn)行等待(wait) 和發(fā)送信號(hào)(signal)這兩種操作。因?yàn)樵贚inux編程中,“等待”和“發(fā)送信號(hào)”都已具有特殊的含義,所以我們將用原先定義的符號(hào)來表示這兩種操作。
- P (信號(hào)量變量):用于等待。
- V (信號(hào)量變量):用于發(fā)送信號(hào)。
這兩個(gè)字母分別來自于荷蘭語單詞passeren(傳遞,就好像位于進(jìn)入臨界區(qū)域之前的檢查點(diǎn))和vrijgeven (給予或釋放,就好像放棄對(duì)臨界區(qū)域的控制權(quán))。在與信號(hào)量關(guān)聯(lián)的內(nèi)容中,你可能還會(huì)看到術(shù)語“開”(up)和“關(guān)”(down),它們?nèi)∽蚤_、關(guān)信號(hào)標(biāo)志的用法。
信號(hào)量的定義
最簡(jiǎn)單的信號(hào)量是只能取值0和1的變量,即二進(jìn)制信號(hào)量。這也是信號(hào)量最常見的一一種形式。可以取多個(gè)正整數(shù)值的信號(hào)量被稱為通用信號(hào)量。
PV操作的定義非常簡(jiǎn)單。假設(shè)有一個(gè)信號(hào)量變量sv,則這兩個(gè)操作的定義如圖:
還可以這樣看信號(hào)量:當(dāng)臨界區(qū)域可用時(shí),信號(hào)量變量sv的值是true,然后P(sv)操作將它減1使它變?yōu)閒alse以表示臨界區(qū)域正在被使用;當(dāng)進(jìn)程離開臨界區(qū)域時(shí),使用V(sv) 操作將它加1,使臨界區(qū)域再次變?yōu)榭捎谩W⒁?#xff0c;只用一個(gè)普通變量進(jìn)行類似的加減法是不行的,因?yàn)樵贑、C++、C#或幾乎任何一個(gè)傳統(tǒng)的編程語言中,都沒有一個(gè)原子操作可以滿足檢測(cè)變量是否為true,如果是再將該變量設(shè)置為false的需要。這也是信號(hào)量操作如此特殊的原因。
信號(hào)量理論例子
我們用一個(gè)簡(jiǎn)單的理論性的例子來說明其工作原理。假設(shè)有兩個(gè)進(jìn)程proc1和proc2,這兩個(gè)進(jìn)程都需要在其執(zhí)行過程中的某–時(shí)刻對(duì)–個(gè)數(shù)據(jù)庫進(jìn)行獨(dú)占式的訪問。我們定義一一個(gè)二進(jìn)制信號(hào)量sv,該變量的初始值為1,兩個(gè)進(jìn)程都可以訪問它。要想對(duì)代碼中的臨界區(qū)域進(jìn)行訪問,這兩個(gè)進(jìn)程都需要執(zhí)行相同的處理步驟,事實(shí)上,這兩個(gè)進(jìn)程可以只是同一個(gè)程序的兩個(gè)不同執(zhí)行實(shí)例。兩個(gè)進(jìn)程共享信號(hào)量變量sv。一旦其中一個(gè)進(jìn)程執(zhí)行了P(sv)操作,它將得到信號(hào)量,并可以進(jìn)入臨界區(qū)域。而第二個(gè)進(jìn)程將被阻止進(jìn)入臨界區(qū)域,因?yàn)楫?dāng)它試圖執(zhí)行P(sv)操作時(shí),它會(huì)被掛起以等待第一一個(gè)進(jìn)程離開臨界區(qū)域并執(zhí)行V(sv)操作釋放信號(hào)量。
需要的偽代碼對(duì)兩個(gè)進(jìn)程都是相同的,如下所示:
這段代碼相當(dāng)簡(jiǎn)單,這是因?yàn)镻V操作的功能非常強(qiáng)大。如圖顯示了Pv操作是如何把守代碼中的臨界區(qū)域的。
Linux信號(hào)量機(jī)制
. 頭文件
#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);
使用信號(hào)量
兩線程完成輸入
#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);//函數(shù)線程完成將用戶輸入的數(shù)據(jù)存儲(chǔ)到文件中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);//主線程完成獲取用戶數(shù)據(jù)的數(shù)據(jù),并存儲(chǔ)在全局?jǐn)?shù)組 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);}總結(jié)
- 上一篇: linux c post上传文件,Lin
- 下一篇: Linux线程的终止