Linux進程間通訊---信號量通訊之semget()、semctl()、semop()

這個信號量理解起來是有點不容易啊,我看書看了好幾遍才知道怎麼回事。在講這一節信號量以前,我仍是想先說幾個小知識點,這也是我在學習完後最終理解的「精華」,哈哈!linux

  信號量是幹啥的?編程

  信號量就是用來解決進程間的同步與互斥問題的一種進程間通訊機制。函數

  同步與互斥的通俗理解學習

   這兩個名詞我們從字面上就能理解。舉個例子吧,在建立子進程時,你是怎麼保證父子進程執行的前後順序呢?我在之前的時候是經過sleep()函數來實現 的,好比我想讓子進程先運行再讓父進程運行,那麼我就在父進程的程序中加一個sleep()函數,讓父進程先睡眠,這樣子就能先執行子進程了。有的時候咱 們事先沒法知道父進程和子進程哪個先執行,可是要向我那樣使用sleep()函數,只能保證先執行子進程,可是不能保證子進程執行完後再執行父進程,這 樣說能理解吧。因此若是咱們想要子進程徹底執行完後再執行父進程,就能夠利用信號量來解決它們之間的同步問題。還不理解也不要緊,我會在後面的實驗中再結 合實際講。再舉個更通俗的例子,一條食品生產線上,假設A、B共同完成一個食品的包裝任務,A負責將食品放到盒子裏,B和C負責將盒子打包。必須得是A先 裝食品B再打包吧,要是B不按規則先打包,那A還裝啥,因此就須要一種機制方法保證A先進行B再進行,「信號量」就是這種機制方法,AB之間的關係就是同 步關係;再假設打包要用到刀子,而車間就有一把刀子,這時候B和C就構成了互斥關係。操作系統

  信號量與信號的區別接口

  不瞞你說, 我剛開始學的時候所理解的就是「信號量是不少個信號」,固然這是錯誤的哈!光從英語上看就不同:信號量是 Semaphore,而信號是 Signal 。其實,信號和信號量是不一樣的。它們雖然均可以實現同步和互斥,可是前者是使用信號處理器來進行的,然後者是使用P,V操做來實現的。(PV操做後邊有 講)隊列

  好了,進入正題。進程

  信號量概述內存

  在多任務操做系統環境下,多個進程會同時運行,而且一些進程間可能會存在必定的關聯。多個進程可能爲了完成同一個任務相互協做,這就造成了進程間的同步關係。並且在不一樣進程間,爲了爭奪有限的系統資源(硬件或軟件資源)會進入競爭狀態,這就是進程間的互斥關係。資源

   進程間的互斥關係與同步關係存在的根源在於臨界資源。臨界資源是在同一時刻只容許有限個(一般只有一個)進程能夠訪問(讀)或修改(寫)的資源,一般包 括硬件資源(處理器、內存、存儲器及其它外圍設備等)和軟件資源(共享代碼段、共享結構和變量等)。訪問臨界資源的代碼叫作臨界區,臨界區自己也會稱爲臨 界資源。

  信號量是用來解決進程間的同步與互斥問題的一種進程間通訊機制,包括一個稱爲信號量的變量和在該信號量下等待資源的進程等待隊 列,以及對信號量進行的兩個原子操做(P/V操做)。其中,信號量對應於某一種資源,取一個非負的整形值。信號量值(經常使用sem_id表示)指的是當前可 用的該資源的數量,若等於0則意味着目前沒有可用的資源。

  PV原子操做(很重要的)

  PV原子操做的具體定義以下:(好好理解,很重要的啊)

  ●  P操做:若是有可用的資源(信號量值>0),則此操做所在的進程佔用一個資源(此時信號量值減1,進入臨界區代碼);若是沒有可用的資源(信號量值=0),則此操做所在的進程被阻塞直到系統將資源分配給該進程(進入等待隊列,一直等到資源輪到該進程)。

  ●  V操做:若是在該信號量的等待隊列中有進程在等待資源,則喚醒一個阻塞進程;若是沒有進程等待它,則釋放一個資源(即信號量值加1)。

  常見的使用信號量訪問臨界區的僞代碼以下圖1:

  

  圖1中的非臨界區和臨界區在我們這裏就是代碼,具體這張圖的理解還要結合着後面的實驗才能理解。

  最簡單的信號量只能取0和1值,這種信號量叫作二維信號量,在本節中,主要討論二維信號量。二維信號量學好了,比較容易擴展到使用多維信號量的狀況。

  信號量編程

  函數說明

  在Linux系統中,使用信號量一般分爲如下4個步驟:

  ①  建立信號量或得到在系統中已存在的信號量,此時須要調用 semget() 函數。不一樣進程經過使用同一個信號量鍵值來得到同一個信號量。

  ②  初始化信號量,此時使用 semctl() 函數的SETVAL操做。當使用二維信號量時,一般將信號量初始化爲1。

  ③  進行信號量的PV操做,此時,調用 semop()函數。這一步是實現進程間的同步和互斥的核心工做部分。

  ④  若是不須要信號量,則從系統中刪除它,此時使用semctl()函數的 IPC_RMID操做。須要注意的是,在程序中不該該出現對已經被刪除的信號量的操做。

  函數格式

  

       返回值:若是成功,則返回信號量集的IPC標識符。若是失敗,則返回-1:errno=EACCESS(沒有權限)
EEXIST(信號量集已經存在,沒法建立)
EIDRM(信號量集已經刪除)
ENOENT(信號量集不存在,同時沒有使用IPC_CREAT)
ENOMEM(沒有足夠的內存建立新的信號量集)
ENOSPC(超出限制)       

        系統調用semget()的第一個參數是關鍵字值(通常是由系統調用ftok()返回的)。系統內核將此值和系統中存在的其餘的信號量集的關鍵 字值進行比較。打開和存取操做與參數semflg中的內容相關。IPC_CREAT若是信號量集在系統內核中不存在,則建立信號量集。IPC_EXCL當 和 IPC_CREAT一同使用時,若是信號量集已經存在,則調用失敗。若是單獨使用IPC_CREAT,則semget()要麼返回新建立的信號量集的標識 符,要麼返回系統中已經存在的一樣的關鍵字值的信號量的標識符。若是IPC_EXCL和IPC_CREAT一同使用,則要麼返回新建立的信號量集的標識 符,要麼返回-1。IPC_EXCL單獨使用沒有意義。參數nsems指出了一個新的信號量集中應該建立的信號量的個數。信號量集中最多的信號量的個數是 在linux/sem.h中定義的:

#define   SEMMSL   32   /*<=512maxnumofsemaphoresperid*/

  

  表2   semctl()函數

 

  

  基礎實驗1

  這兩個實驗主要是練習熟悉一下信號量的概念和基本用法,首先,我先在實驗1的代碼中不添加與信號量相關的代碼,觀察運行結果,實驗代碼以下

  simple_fork.c文件點此下載

  

  編譯運行結果以下

  

  這個運行結果是有點意思哈,由結果能夠看到父進程先結束,而後子進程結束,可是我本意不是這樣啊,我想讓子進程先執行,父進程再執行,也就是父進程等待子進程結束。下面我們就用信號量來實現它。

  基礎實驗2

   本實驗使用信號量來解決上面實驗1的多進程間存在的同步問題,完成的功能是使父進程等待子進程結束。由於信號量相關的函數調用接口比較複雜,我們將它們 封裝成二維單個信號量的基本函數,分別爲信號量初始化函數(或者信號量賦值函數)init_sem()、P操做函數sem_p()、V操做函數 sem_v()及刪除信號量函數 del_sem()等,具體實驗代碼以下

  sem_fork.c文件點此下載

  

  

  

  

  編譯運行結果以下

  

  這樣經過信號量就保證了它們之間的同步問題

  實驗分析

  下面再進一步結合實驗結果重點理解一下 P/V操做,下面再貼出來圖1

  

  由實驗2的第47~49行能夠看出,47行的 P操做和49行的 V操做決定了48行的代碼爲圖1中的臨界區(資源R)。

   程序執行流程就是,先將信號量值初始化爲0(第31行),假設程序先執行父進程,經過前邊講的P操做的特色可知,因爲信號量值爲0,該進程被阻塞,也就 是轉而去執行子進程,由V操做可知,執行完子進程後,因爲信號量的等待隊列中有進程(這裏是父進程)在等待資源,則喚醒一個阻塞進程(這裏是父進程)。

相關文章
相關標籤/搜索