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 的不同取值及操作如下所示:

如果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;
}

運行結果如下:

首先,我們能過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秒程序就成功退出了。

 

 

參考:

http://blog.csdn.net/ljianhui/article/details/10130539

《Linux 高性能服務器編程》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM