linux下訊號量詳解

2021-10-03 15:13:42 字數 4471 閱讀 5708

訊號量:

訊號量實際上就是乙個計數器,用於控制程序或執行緒對臨界資源的同步與互斥,對訊號量的操作是乙個原子操作(也就是在乙個程序或執行緒訪問它時,其它的程序或執行緒不能同時訪問它來打斷前乙個程序或執行緒的操作),它的原理就是在乙個程序或執行緒過來時,先訪問訊號量,如果這個訊號量大於0,則讓計數-1,然後讓這個程序或執行緒執行下面的操作,如果這個訊號量小於等於0,則阻塞此程序或執行緒讓它陷入等待,直到此訊號量計數加1被喚醒(喚醒時的操作是,先讓訊號量計數加1,然後喚醒等待的程序或執行緒,如果此時計數大於0,則讓此程序或執行緒執行下面的操作)

訊號量的使用:

要想使用訊號量,就得先建立訊號量,linux提供了乙個建立訊號的型別,它就是sem_t(直接用它像建立變數一樣建立訊號量),建立完之後還不能使用,因為它現在是乙個隨機的屬性,屬於未定義行為

所以我們就得給建立好的訊號量進行初始化,linux提供初始化函式是sem_init(這個函式有三個引數,第乙個引數是你要設定的訊號量,不過傳的時候要傳位址,它是乙個sem_t的指標型別,第二個引數是你要對程序還是執行緒進行控制,如果傳入0,則表示用於執行緒間的同步與互斥,如果傳入的非0,則表示用於程序間的同步與互斥,這個引數是乙個整形,第三個引數是設定訊號量的初值,因為訊號量的本質是乙個計數器,如果初值設為1,那麼和互斥鎖的功能差不多,這個引數是乙個無符號的整形),如果這個函式初始化成功則返回0,如果失敗則返回-1

然後我們就可以使用訊號量了,我們可以在需要保證安全實現互斥的地方使用訊號來進行判斷阻塞,如果如果這個訊號量大於0,則讓計數-1,然後讓這個程序或執行緒執行下面的操作,如果這個訊號量小於等於0,則阻塞此程序或執行緒讓它陷入等待,直到此訊號量計數加1被喚醒,linux提供的函式是sem_wait(這個函式只有乙個引數,就是你要使用訊號量的位址,這個引數是乙個sem_t的指標型別),如果這個函式成功則返回0,如果失敗則返回-1;還有乙個函式sem_trywait,它和sem_wait的區別是,如果訊號量小於等於0,則直接報錯返回,而不是等待,它的引數資訊和sem_wait相同

當我們有資源時,就可以喚醒等待的程序或執行緒了,這個時候執行的操作就是讓計數加1,然後喚醒那些等待的程序或執行緒,linux提供的函式是sem_post(這個函式只有乙個引數,就是你要喚醒的訊號量的位址,這個引數是乙個sem_t的指標型別),如果這個函式成功則返回0,如果失敗則返回-1

最後要記得,在使用完訊號量後要刪除它,因為它並不是棧空間裡的變數,不會自己刪除,linux給我們提供的函式是sem_destroy(這個函式只有乙個引數,就是你要刪除的訊號量的位址,這個引數是乙個sem_t的指標型別),如果這個函式成功則返回0,如果失敗則返回-1

訊號量集的使用:

有時我們可能需要用到很多訊號量,這時我們如果在**中建立很多訊號量會顯得**冗餘,這時我們就可以使用訊號量集了,它的原理也很簡單,就是在在乙個檔案中建立很多訊號量,然後通過乙個函式一直進行管理和使用,這樣我們在使用訊號量時,就會顯得比較簡潔,在訊號量集中,是通過p、v進行操作的,p代表如果這個訊號量的值小於0,則讓後面的程序或執行緒陷入等待,將它的pcb插入到佇列的隊尾,v表示如果這個訊號量的值小於等於0,則讓計數加1,並喚醒乙個程序或執行緒,並讓程序或執行緒進入就緒狀態;就是訊號量的pv操作,在使用p、v時,只要將訊號量的semid號傳入即可)下面介紹一些重點的函式

它和共享記憶體相同都需要通過ftok來產生乙個唯一標識的key_t的檔案資訊,ftok(這個函式一共兩個引數,第乙個引數是那個普通檔案的路徑,是乙個const型的字元指標,第二個引數是乙個整形id號,不過這個整形只能使用前8位,也就是說這個id最大可以表示255,只超出8位,那麼就不管8位之後的數,只計算8位前的二進位制,這個id號也是有很大作用,因為不同的檔案系統都會有自己的一套inode號,系統找檔案就是在找inode號,所以可能出現不同的檔案具有相同的inode號,因為最後都是要改造成共享記憶體的特殊檔案,所以將檔案拿進來後,可能兩個檔案的inode號相同,因為sem這個檔案系統不會重新分配inode號(不重新分配的原因是使用者可能會自己開啟這個檔案,如果inode號變的話,就會開啟錯誤),所以需要乙個id來標識檔案,所以就有了這個引數),這個函式如果成功則返回值是乙個key_t型別的值,我稱為ipc鍵,它的作用是代表著乙個已經可以被訊號量集安全使用的檔案,如果失敗則返回-1;另外這個函式有乙個坑,就是我們一般認為,只要建立時檔案路徑名和id號相同,則訪問就相同的訊號量集檔案,但其實如果這個檔案在之前被刪除過,然後又建立了乙個同名的檔案,然後使用ftok來建立訊號量集檔案,就會出錯,因為在刪除後重新建立檔案它的inode號會變,所以ftok出來的檔案就不對了;

然後我們就可以使用semget這個函式來建立這個訊號量集了(這個函式一共三個引數,第乙個引數是ipc鍵,用來確定要建立的訊號量集的位置,這個引數的型別當然也key_t了,第二個引數是你要建立訊號量的個數,這個引數是乙個整形,第三個引數是乙個選項標誌,它是用來控制訊號量集檔案的,有時我們不想先在外部直接建立好檔案,不用ftok函式,所以這個引數就是為了解決這個問題,它有二個巨集,第乙個是ipc_creat,它表示這個檔案存在則開啟,如果不存在則建立,第二個巨集是ipc_excl,它表示如果檔案不存在則建立,如果存在則讓這個函式返回-1報錯,一般和ipc_creat配合使用,保證這個檔案一定是新建立的,最後這個引數依然可以用或的方式來指定檔案許可權),這個函式如果成功則返回這個訊號量檔案的操作控制代碼是乙個整形,和檔案描述符差不多,用來操作訊號量檔案,如果失敗則返回-1

然後我們就可以對這個訊號量集進行操作,linux提供的函式是semctl(這個函式有四個引數,不過可以只用三個引數,第乙個引數就是這個訊號量集檔案的操作控制代碼,這個引數是乙個int型別,第二個引數是你要操作訊號量集中的某個訊號量的序號,這個引數也是乙個int型別,第三個引數是接下來要採取的動作有幾個巨集,第乙個是巨集是ipc_rmid,表示刪除訊號量集,喚醒所有阻塞的程序或執行緒,第二個巨集是ipc_stat,表示將此訊號量集的狀態資訊傳送到第四個引數的用於接收此狀態的結構體中,第三個巨集是ipc_set,表示將第四個引數中儲存的狀態來替換此時這個訊號量集的狀態,第四巨集是setval,表示用第四個引數中儲存的訊號量計數值來重新設定第二個引數中訊號量計數值,第五個巨集是getval,將此訊號量集中第二個引數的訊號量的計數個數儲存到第四個引數中對應的引數中去,還有其它幾個巨集不一一枚舉;第四個引數乙個聯合體型別union semun,這個聯合體並沒有在標頭檔案中定義,如果要用到這個引數需要使用者自己來定義這個結構體

union semun ;

這個函式如果操作成功則返回0,如果失敗則返回-1

上面的的函式可以控制單個訊號量的使用也可對整個訊號量集做出操作,不過上面的函式主要是為了訊號量集的狀態或刪除操作,但要想整體對訊號量集中的所有訊號量做出操作,或單個訊號進行操作(如所有訊號量的初始化和改所有訊號量的值)不太好實現,所以linux提供了另乙個對訊號量的資源控制函式semop(這個函式一共有三個引數,第乙個引數是ipc鍵,用來確定要建立的訊號量集的位置,這個引數的型別當然也key_t了;第二個引數是乙個結構體陣列指標,這個結構體是struct semop,這個結構體已經在標頭檔案中定義,我們可以直接定義出陣列來使用,它的目的就是控制單個訊號量的使用,每個訊號量都有這麼乙個結構體,在訊號量集中,第乙個訊號量的結構體就是陣列中的第0個元素,對這個結構體進行初始化後,訊號量並不會立即進行使用,需要通過這個函式來讓訊號量知道它要進行什麼操作,所以這個引數就是要把這個結構體陣列的首位址傳入,這個結構體如:

struct sembuf;

sem_op這個引數是用來告訴訊號量應該執行哪個操作,如果這個引數是大於0的乙個數,那麼就讓semval(訊號量現在的值)加上這個數,表示允許多個程序或執行緒進行使用,另外如果sem_***設定了sem_undo那麼semadj將的值將會減去這個數(對某個程序,在指定sem_undo後,對訊號量semval值的修改都會反應到semadj上,當該程序終止的時候,核心會根據semadj的值,重新恢復訊號量之前的值;也就是semadj是為了還原semval之前的值,也就是保留上一次semval的值,相當於拿semval加了這個值後的值再減去這個值,然後賦給semadj)

如果sem_op是等於0的,那麼就代表使用者希望semval的值為0,如果為0則立即返回,如果不為0相應訊號量的semzcnt(它代表等待semval變為0的程序或執行緒數,也就是阻塞了多少個程序或執行緒)加1,呼叫的程序或執行緒阻塞等待

如果sem_op是小於0 的,當sem_op(其實這裡可以感覺到是為了保證讓一批程序或執行緒同時進行操作)的絕對值是小於semval的值時,那麼讓semval減去sem_op的絕對值,表示將sem_op數量的程序或執行緒進行使用,分配資源,然後剩下的就是接下來允許程序或執行緒使用的數目,如果sem_op的絕對值是大於semval的,那麼就讓semncant(表示等待semval變為大於sem_op絕對值的程序或執行緒數,也就是被阻塞的程序或執行緒數)加1,然後讓程序或執行緒阻塞等待,直到semval的值大於sem_op絕對值後,再減去它,分配資源,以保證有一批程序或執行緒可以不被阻塞同時使用

sem_***這個標誌有二個巨集,先說不是巨集的,如果將這個位置設為0,代表正常使用,沒有特殊操作,如果設為ipc_wait,代表這個訊號量在使用時如果不滿足條件,將不是阻塞,而是直接報錯返回-1,如果設為sem_undo表示維護程序或執行緒對訊號量的調整值,以便程序或執行緒結束後(不論是正常結束還是異常結束,主要是為了異常結束後能得到資訊)能恢復之前訊號量的狀態

這個函式的第三個引數是乙個整形,表示對你傳入的結構體陣列首位址以下的幾個訊號量進行操作)

如果這個函式成功則返回0,失敗則返回-1

Linux訊號量詳解

1.什麼是訊號量 訊號量是一種特殊的變數,訪問具有原子性。只允許對它進行兩個操作 1 等待訊號量 當訊號量值為0時,程式等待 當訊號量值大於0時,訊號量減1,程式繼續執行。2 傳送訊號量 將訊號量值加1。我們使用訊號量,來解決程序或執行緒間共享資源引發的同步問題。2.linux中訊號量的使用 lin...

Linux訊號量詳解

1.什麼是訊號量 訊號量是一種特殊的變數,訪問具有原子性。只允許對它進行兩個操作 1 等待訊號量 當訊號量值為0時,程式等待 當訊號量值大於0時,訊號量減1,程式繼續執行。2 傳送訊號量 將訊號量值加1。我們使用訊號量,來解決程序或執行緒間共享資源引發的同步問題。2.linux中訊號量的使用 lin...

Linux下的訊號量

linux下的訊號量本身就是臨界資源,所以pv操作都是原子操作。下面是實現二元訊號量的 二元訊號量就是互斥鎖。訊號量 中的semop函式是進行pv操作的核心函式,semop的函式原型為 int semop int semid,struct sembuf sops,unsigned nsops 第乙個...