linux內核學習之信號量操作

轉自http://blog.sina.com.cn/s/blog_803527e70100vd4f.html

轉自https://www.cnblogs.com/guochuanrui/p/5677570.html

信號量的操作——semop函數

     信號量的值與相應資源的使用情況有關,當它的值大於 0 時,表示當前可用的資源數的數量;當它的值小於 0 時,其絕對值表示等待使用該資源的進程個數。信號量的值僅能由 PV 操作來改變。

 

     在 Linux 下,PV 操作通過調用semop函數來實現。該函數定義在頭文件 sys/sem.h中,原型如下:

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

     函數的參數 semid 爲信號量集的標識符;參數 sops 指向進行操作的結構體數組的首地址;參數 nsops 指出將要進行操作的信號的個數。semop 函數調用成功返回 0,失敗返回 -1。

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

     struct  sembuf{

         unsigned short   sem_num;      //信號在信號集中的索引,0代表第一個信號,1代表第二個信號  

         short            sem_op;      //操作類型

         short            sem_flg;    //操作標誌

     };

    下面詳細介紹一下 sembuf 的幾個參數:

--------------------------------------------------------------------------------------------------

  • sem_op 參數

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

 

                    sem_op = 0          如果沒有設置 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,它將會自動撤消該進程終止時。

        在標準操作程序中的操作是在數組的順序執行、原子的,那就是,該操作要麼作爲一個完整的單元,要麼不。如果不是所有操作都可以立即執行的系統調用的行爲取決於在個人sem_flg領域的IPC_NOWAIT標誌的存在。
 -------------------------------------------------------------------------------------------------     
        對信號量最基本的操作就是進行PV操作,而System V信號量正是通過 semop 函數和 sembuf 結構體的數據結構來進行PV操作的。

        當 sembuf 的第二個數據結構 sem_op 設置爲負數時,是對它進行P操作,即減1操作;當設置爲正數時,就是進行V操作,即加1操作。

      

        下面舉一個對一個信號量集中的某個信號進行 PV 操作的函數實現:

         

        //P操作函數

        int  sem_p( int semid, int index )

        {

                  struct  sembuf  buf  = { 0, -1, IPC_NOWAIT};

                  

                  if ( index < 0 )

                  {

                                 perror ( "index of array cannot equals a minus value!\n" );

                                 return  -1;

                  }

                  buf.sem_num = index;

                  if ( semop ( semid, &buf, 1) == -1)

                  {

                                perroe ( " a wrong operation to semaphore occurred!\n" );

                                return  -1;

                  }

                  return  0;

        }

 

        //V操作函數

        int  sem_p( int semid, int index )

        {

                  struct  sembuf  buf  = { 0, 1, IPC_NOWAIT};

                  

                  if ( index < 0 )

                  {

                                 perror ( "index of array cannot equals a minus value!\n" );

                                 return  -1;

                  }

                  buf.sem_num = index;

                  if ( semop ( semid, &buf, 1) == -1)

                  {

                                perroe ( " a wrong operation to semaphore occurred!\n" );

                                return  -1;

                  }

                  return  0;

        }

     

信號量 sem_undo設置

  一 爲什麼要使用信號量

  爲了防止出現因多個程序同時訪問一個共享資源而引發的一系列問題,我們需要一種方法,它可以通過生成並使用令牌來授權,在任一時刻只能有一個執行線程訪問 代碼的臨界區域。臨界區域是指執行數據更新的代碼需要獨佔式地執行。而信號量就可以提供這樣的一種訪問機制,讓一個臨界區同一時間只有一個線程在訪問它, 也就是說信號量是用來調協進程對共享資源的訪問的。其中共享內存的使用就要用到信號量

  二 信號量的工作原理

  由於信號量只能進行兩種操作等待和發送信號,即P(sv)和V(sv),他們的行爲是這樣的:

  P(sv):如果sv的值大於零,就給它減1;如果它的值爲零,就掛起該進程的執行

  V(sv):如果有其他進程因等待sv而被掛起,就讓它恢復運行,如果沒有進程因等待sv而掛起,就給它加1.

 

  舉個例子,就是 兩個進程共享信號量sv,一旦其中一個進程執行了P(sv)操作,它將得到信號量,並可以進入臨界區,使sv減1。而第二個進程將被阻止進入臨界區,因爲 當它試圖執行P(sv)時,sv爲0,它會被掛起以等待第一個進程離開臨界區域並執行V(sv)釋放信號量,這時第二個進程就可以恢復執行。

  三 Linux的信號量機制

  Linux提供了一組精心設計的信號量接口來對信號進行操作,它們不只是針對二進制信號量,下面將會對這些函數進行介紹,但請注意,這些函數都是用來對成組的信號量值進行操作的。它們聲明在頭文件sys/sem.h中。

  四 信號號相關的兩個結構體

  內核爲每個信號量集合設置了一個semid_ds結構

  

複製代碼

struct semid_ds {
struct ipc_permsem_perm ;
structsem* sem_base ; //信號數組指針
ushort sem_nsem ; //此集中信號個數
time_t sem_otime ; //最後一次semop時間
time_t sem_ctime ; //最後一次創建時間
} ;
每個信號量由一個無名結構表示,它至少包含下列成員: (這個是什麼意思??)
struct {
ushort_t semval ; //信號量的值
short sempid ; //最後一個調用semop的進程ID
ushort semncnt ; //等待該信號量值大於當前值的進程數(一有進程釋放資源 就被喚醒)
ushort semzcnt ; //等待該信號量值等於0的進程數
} ;

複製代碼

 

  五 信號量的使用

  1、創建信號量

  semget函數創建一個信號量集或訪問一個已存在的信號量集。

  #include <sys/sem.h>

  int semget (key_t key, int nsem, int oflag) ;

  返回值是一個稱爲信號量標識符的整數,semop和semctl函數將使用它。

  參數nsem指定集合中的信號量數。(若用於訪問一個已存在的集合,那就可以把該參數指定爲0)

  參數oflag可以是SEM_R(read)和SEM_A(alter)常值的組合。(打開時用到),也可以是IPC_CREAT或IPC_EXCL ;

 

  2、打開信號量

  使用semget打開一個信號量集後,對其中一個或多個信號量的操作就使用semop(op--operate)函數來執行。

  #include <sys/sem.h>

  int semop (int semid, struct sembuf * opsptr, size_t nops) ;

  參數opsptr是一個指針,它指向一個信號量操作數組,信號量操作由sembuf結構表示:

 

複製代碼

struct sembuf{
short sem_num; // 除非使用一組信號量,否則它爲0
short sem_op; // 信號量在一次操作中需要改變的數據,通常是兩個數,
// 一個是-1,即P(等待)操作,一個是+1,即V(發送信號)操作
short sem_flg; // 通常爲SEM_UNDO,使操作系統跟蹤信號,並在進程沒有釋放該信號量而終止時,
// 操作系統釋放信號量
};

複製代碼

 

  參數nops規定opsptr數組中元素個數。

  sem_op值:

  (1)若sem_op爲正,這對應於進程釋放佔用的資源數。sem_op值加到信號量的值上。(V操作)

  (2)若sem_op爲負,這表示要獲取該信號量控制的資源數。信號量值減去sem_op的絕對值。(P操作)

  (3)若sem_op爲0,這表示調用進程希望等待到該信號量值變成0

  如果信號量值小於sem_op的絕對值(資源不能滿足要求),則:

  (1)若指定了IPC_NOWAIT,則semop()出錯返回EAGAIN。

  (2)若未指定IPC_NOWAIT,則信號量的semncnt值加1(因爲調用進程將進 入休眠狀態),然後調用進程被掛起直至:①此信號量變成大於或等於sem_op的絕對值;②從系統中刪除了此信號量,返回EIDRM;③進程捕捉到一個信 號,並從信號處理程序返回,返回EINTR。(與消息隊列的阻塞處理方式 很相似)

   3、信號量是操作

  semctl函數對一個信號量執行各種控制操作。

  #include <sys/sem.h>

  int semctl (int semid, int semnum, int cmd, /*可選參數*/ ) ;

  第四個參數是可選的,取決於第三個參數cmd。

  參數semnum指定信號集中的哪個信號(操作對象)

  參數cmd指定以下10種命令中的一種,在semid指定的信號量集合上執行此命令。

  IPC_STAT   讀取一個信號量集的數據結構semid_ds,並將其存儲在semun中的buf參數中。

  IPC_SET     設置信號量集的數據結構semid_ds中的元素ipc_perm,其值取自semun中的buf參數。

  IPC_RMID  將信號量集從內存中刪除。

  GETALL      用於讀取信號量集中的所有信號量的值。

  GETNCNT  返回正在等待資源的進程數目。

  GETPID      返回最後一個執行semop操作的進程的PID。

  GETVAL      返回信號量集中的一個單個的信號量的值。

  GETZCNT   返回這在等待完全空閒的資源的進程數目。

  SETALL       設置信號量集中的所有的信號量的值。

  SETVAL      設置信號量集中的一個單獨的信號量的值。

  六 信號量值的初始化

  semget並不初始化各個信號量的值,這個初始化必須通過以SETVAL命令(設置集合中的一個值)或SETALL命令(設置集合中的所有值) 調用semctl來完成。

  SystemV信號量的設計中,創建一個信號量集並將它初始化需兩次函數調用是一個致命的缺陷。一個不完備的解決方案是:在調用semget時指定IPC_CREAT | IPC_EXCL標誌,這樣只有一個進程(首先調用semget的那個進程)創建所需信號量,該進程隨後初始化該信號量。

 

  SEM_UNDO

  當操作信號量(semop)時,sem_flg可以設置SEM_UNDO標識;SEM_UNDO用於將修改的信號量值在進程正常退出(調用exit退出或main執行完)或異常退出(如段異常、除0異常、收到KILL信號等)時歸還給信號量。
  如信號量初始值是20,進程以SEM_UNDO方式操作信號量減2,減5,加1;在進程未退出時,信號量變成20-2-5+1=14;在進程退出時,將修改的值歸還給信號量,信號量變成14+2+5-1=20。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章