天天看點

Linux程序間通信(二):信号集函數 sigemptyset()、sigprocmask()、sigpending()、sigsuspend()

我們已經知道,我們可以通過信号來終止程序,也可以通過信号來在程序間進行通信,程式也可以通過指定信号的關聯處理函數來改變信号的預設處理方式,也可以屏蔽某些信号,使其不能傳遞給程序。那麼我們應該如何設定我們需要處理的信号,我們不需要處理哪些信号等問題呢?信号集函數就是幫助我們解決這些問題的。

有關Linux程序間使用信号通信的更多内容,可以參閱我的另一篇文章,​​Linux程序間通信 -- 信号量函數 signal()、sigaction()​​

下面是信号函數集:

1、int sigemptyset(sigset_t *set);

該函數的作用是将信号集初始化為空。

2、int sigfillset(sigset_t *set);

該函數的作用是把信号集初始化包含所有已定義的信号。

3、int sigaddset(sigset_t *set, int signo);

該函數的作用是把信号signo添加到信号集set中,成功時傳回0,失敗時傳回-1。

4、int sigdelset(sigset_t *set, int signo);

該函數的作用是把信号signo從信号集set中删除,成功時傳回0,失敗時傳回-1.

5、int sigismember(sigset_t *set, int signo);

該函數的作用是判斷給定的信号signo是否是信号集中的一個成員,如果是傳回1,如果不是,傳回0,如果給定的信号無效,傳回-1;

6、int sigpromask(int how, const sigset_t *set, sigset_t *oset);

該函數可以根據參數指定的方法修改程序的信号屏蔽字。新的信号屏蔽字由參數set(非空)指定,而原先的信号屏蔽字将儲存在oset(非空)中。如果set為空,則how沒有意義,但此時調用該函數,如果oset不為空,則把目前信号屏蔽字儲存到oset中。

how 的不同取值及操作如下所示:

Linux程式間通信(二):信号集函數 sigemptyset()、sigprocmask()、sigpending()、sigsuspend()

如果sigpromask成功完成傳回0,如果how取值無效傳回-1,并設定errno為EINVAL。

注意:調用這個函數才能改變程序的屏蔽字,之前的函數都是為改變一個變量的值而已,并不會真正影響程序的屏蔽字。

7、int sigpending(sigset_t *set);

該函數的作用是将被阻塞的信号中停留在待處理狀态的一組信号寫到參數set指向的信号集中,成功調用傳回0,否則傳回-1,并設定errno表明錯誤原因。

8、int sigsuspend(const sigset_t *sigmask);

該函數通過将程序的屏蔽字替換為由參數sigmask給出的信号集,然後挂起程序的執行。注意操作的先後順序,是先替換再挂起程式的執行。程式将在信号處理函數執行完畢後繼續執行。如果接收到信号終止了程式,sigsuspend()就不會傳回,如果接收到的信号沒有終止程式,sigsuspend()就傳回-1,并将errno設定為EINTR。

特别提醒:如果一個信号被程序阻塞,它就不會傳遞給程序,但會停留在待處理狀态,當程序解除對待處理信号的阻塞時,待處理信号就會立刻被處理。

下面以一個例子來說明上述函數的用法,源檔案為 sigset.c,代碼如下:

#include <stdio.h>​​
​​#include <signal.h>​​
​​#include <unistd.h>​​
 
​​void​​​ ​​handler(​​​​int​​​ ​​sig)​​
​​{​​
​​printf​​​​(​​​​"Handle the signal %d\n"​​​​, sig);​​
​​}​​
 
​​int​​​ ​​main(​​​​int​​​ ​​argc, ​​​​char​​​ ​​**argv)​​
​​{​​
​​sigset_t sigset;    ​​​​// 用于記錄屏蔽字​​
​​sigset_t ign;       ​​​​// 用于記錄被阻塞(屏蔽)的信号集​​
​​struct​​​ ​​sigaction act;​​
 
​​// 清空信号集​​
​​sigemptyset(&sigset);​​
​​sigemptyset(&ign);​​
 
​​// 向信号集中添加 SIGINT​​
​​sigaddset(&sigset, SIGINT);​​
 
​​// 設定處理函數 和 信号集​​
​​act.sa_handler = handler;​​
​​sigemptyset(&act.sa_mask);​​
​​act.sa_flags = 0;​​
​​sigaction(SIGINT, &act, 0);​​
 
​​printf​​​​(​​​​"Wait the signal SIGNAL...\n"​​​​);​​
​​pause();​​
 
​​// 設定程序屏蔽字, 在本例中為屏蔽 SIGINT​​
​​sigprocmask(SIG_SETMASK, &sigset, 0);​​
​​printf​​​​(​​​​"Please press Ctrl + C in 10 seconds...\n"​​​​);​​
​​sleep(10);​​
 
​​// 測試 SIGINT 是否被屏蔽​​
​​sigpending(&ign);​​
​​if​​​ ​​(sigismember(&ign, SIGINT))​​
​​{​​
​​printf​​​​(​​​​"The SIGINT signal has ignored\n"​​​​);​​
​​}​​
 
​​// 從信号集中删除信号 SIGINT​​
​​sigdelset(&sigset, SIGINT);​​
​​printf​​​​(​​​​"Wait the signal SIGINT...\n"​​​​);​​
 
​​// 将程序的屏蔽字重新設定, 即取消對 SIGINT 的屏蔽​​
​​// 并挂起程序​​
​​sigsuspend(&sigset);​​
 
​​printf​​​​(​​​​"The app will exit in 5 secondes!\n"​​​​);​​
​​sleep(5);​​
 
​​return​​​ ​​0;​​
​​}​​      

運作結果如下:

Linux程式間通信(二):信号集函數 sigemptyset()、sigprocmask()、sigpending()、sigsuspend()

首先,我們能過sigaction()函數改變了SIGINT信号的預設行為,使之執行指定的函數handler,是以輸出了語句:Handle the signal 2。然後,通過sigprocmask()設定程序的信号屏蔽字,把SIGINT信号屏蔽起來,是以過了10秒之後,用sigpending()函數去擷取被阻塞的信号集時,檢測到了被阻塞的信号SIGINT,輸出The SIGINT signal has ignored。最後,用函數sigdelset()函數去除先前用sigaddset()函數加在sigset上的信号SIGINT,再調用函數sigsuspend(),把程序的屏蔽字再次修改為sigset(不包含SIGINT),并挂起程序。由于先前的SIGINT信号停留在待處理狀态,而現在程序已經不再阻塞該信号,是以程序馬上對該信号進行處理,進而在最後,你不用輸入 Ctrl+C 也會出現後面的處理語句(可參閱前面特别提醒的内容),最後過了5秒程式就成功退出了。

繼續閱讀