# 信號量相關函數解析

信號量一般用于進程之間通信,在c++關于信號量及其相關函數的定義均聲明在signal.h中(linux系統)。信號集是信號量的集合,可以使用sigset_t進行定義,我們一般需要關注的信號集為阻塞集與未決集,阻塞集指明將哪些信號阻塞,集合中對應位置1,表示阻塞該信號。未決集指明哪些信號還未處理,對應位為1,指該信號還未從處理。

sigaction()函數

該函數的作用對象為單個信號,功能是對信號作出相應的處理動作,函數原型為

int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

  • 第一個參數是要捕獲的信號,比如SIGCHLD就是子進程死亡之后發出的信號,填入之后,父進程就會監視并捕獲該信號以作出相應的動作。
  • 第二個參數是一個結構體(下文會詳細解釋該結構體),其主要的作用就是指明捕獲到信號之后要執行的動作。
  • 第三個參數是保存與該信號之前的動作,如果不保存可以直接置空。

sigaction的結構體聲明如下:

       struct sigaction {
           void     (*sa_handler)(int);
           void     (*sa_sigaction)(int, siginfo_t *, void*);
           sigset_t   sa_mask;
           int        sa_flags;
           void     (*sa_restorer)(void);
       };

對于上述的結構體我們只關注三個屬性:

  • void (*sa_handler)(int):該函數指針指向與信號綁定的動作函數,即只要信號被捕獲,就執行該函數指針指向的函數。
  • sigset_t sa_mask:設置阻塞集,表示執行信號動作時需要阻塞哪些信號,在c++里默認新到來的信號會阻塞之前的信號,也就是說如果進程正在執行信號A的處理函數,此時又捕獲到一個信號B,B會將A阻塞,進程會暫停A的處理函數轉而執行B的處理函數,之后再回到斷口處,繼續執行A的處理函數。如果希望在處理A的處理函數時不被打斷,就需要設置阻塞集,表示阻塞那些函數,如果不需要阻塞,可以使用sigemptyset()將阻塞集置空就好。
  • int sa_flags:特殊標志,一般置0,如果置1,會選擇另一種結構體,不予考慮。

信號集相關函數

上節描述了如何使用sigaction捕獲信號并觸發相應的動作,但是在一個進程中往往不止有一個信號,這就涉及信號的阻塞和排隊問題,默認情況下,新捕獲的信號會打斷舊信號的處理函數的執行,當然,signal.h頭文件中定義了一系列關于信號集的操作,來幫助我們順暢的管理信號集。

int sigemptyset(sigset_t *set)

該函數將傳入的信號集所有位初始化為0,一般用于信號集的初始化,不做修改將不阻塞而任何信號。

int sigfillset(sigset_t *set)

該函數將傳入的信號集所有初始化為1,一般用于初始化,不做修改將阻塞所有信號。

int sigaddset(sigset_t *set, int signum)

該函數將指定信號加入信號集,參數一為傳入的信號集,參數二signum為指定的要加入到set中的信號。

int sigdelset(sigset_t *set, int signum)

該函數將指定信號從信號集中刪除,即將信號signum從信號集set中刪除

int sigismember(const sigset_t *set, int signum)

該函數判斷傳入的信號集中上是否包含某特定信號,即判斷信號集set中是否包含信號signum

sigprocmask

上一小節中介紹了信號集與其基本的操作(包括初始化、插入、刪除等),我們創建信號集的目的是為了幫助流暢的捕獲信號,所以我們需要把創建好的信號集作為一種配置來控制信號的捕獲順序,以免信號的默認中斷機制打斷我們對某一信號的持續捕獲。signal.h允許我們使用sigprocmask函數將自定義信號集作為阻塞的配置。該函數原型為:

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset)

參數解析

  • int how:該參數表示操作類型,即要對傳入的信號集做什么操作,是將信號集加入阻塞,還是為該信號集中的信號清除阻塞狀態,該參數有三個值可選:SIG_BLOCK--表示求set與原生的信號集的并集作為阻塞集,即將set中的信號加入阻塞;SIG_UNBLOCK--表示求原生信號集與set的差集作為阻塞集使用,即將set中的信號清除阻塞狀態。SIG_SETMASK--將set信號集將原生的阻塞信號集覆蓋,直接作為阻塞集使用(用的很少)。
  • const sigset_t *set:傳入的自定義的信號集
  • sigset_t *oldset:該參數保存原生的信號阻塞集,不保存可以置空

代碼演示

#include <unistd.h>
#include <iostream>
#include <signal.h>
#include <wait.h>

void alprint(int num){         //信號處理函數
    for(int i=0;i<num;++i){
        sleep(1);
        std::cout<<num<<"  "<<i<<std::endl;
    }
}


int main(){
    sigset_t set;//創建信號集
    sigemptyset(&set);//將信號集初始化為空
    sigaddset(&set,SIGINT);//將鍵盤中斷信號(ctrl-c)加入信號集
    sigprocmask(SIG_BLOCK,&set,NULL);//加入阻塞集
     
    struct sigaction act;  //定義信號處理結構體
    act.sa_handler = alprint; //綁定信號處理函數
    sigemptyset(&act.sa_mask); //清空阻塞集
    act.sa_flags = 0;  
    int num;                  //要傳入的信號
    std::cin>>num;
    sigaction(num,&act,NULL); //捕獲信號,并執行處理程序
    raise(num);                //發出信號
    return 0;
    
}


在上述程序中,我們將鍵盤中斷信號(ctrl-c)加入阻塞集,然后對我們鍵入的數字num進行捕捉并打印numnum的值,并且打印期間不會被ctrl-c打斷。實驗步驟:運行上面的程序(linux下),輸入一個數字(稍微大點,好觀察)然后回車,發現終端在打印剛剛輸入的數字,此時按下ctrl-c,發現無法打斷程序的執行,實驗結束(可以按ctrl-z來結束程序)。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。