linux進程通信之信號

本節主要學習信號和與信號相關的處理函數,後續還會更新。

http://blog.csdn.net/xiaoliangsky/article/details/40264151

一 信號
信號是UNIX和Linux系統響應某些條件而產生的一個事件,接收到該信號的進程會相應地採取一些行動。通常信號是由一個錯誤產生的。但它們還可以作爲進程間通信或修改行爲的一種方式,明確地由一個進程發送給另一個進程。一個信號的產生叫生成,接收到一個信號叫捕獲。
二 信號的種類
Signal         Description
SIGABRT   由調用abort函數產生,進程非正常退出
SIGALRM   用alarm函數設置的timer超時或setitimer函數設置的interval timer超時
SIGBUS    某種特定的硬件異常,通常由內存訪問引起
SIGCANCEL 由Solaris Thread Library內部使用,通常不會使用
SIGCHLD   進程Terminate或Stop的時候,SIGCHLD會發送給它的父進程。缺省情況下該Signal會被忽略
SIGCONT   當被stop的進程恢復運行的時候,自動發送
SIGEMT    和實現相關的硬件異常
SIGFPE    數學相關的異常,如被0除,浮點溢出,等等
SIGFREEZE Solaris專用,Hiberate或者Suspended時候發送
SIGHUP    發送給具有Terminal的Controlling Process,當terminal被disconnect時候發送
SIGILL    非法指令異常
SIGINFO   BSD signal。由Status Key產生,通常是CTRL+T。發送給所有Foreground Group的進程
SIGINT    由Interrupt Key產生,通常是CTRL+C或者DELETE。發送給所有ForeGround Group的進程
SIGIO     異步IO事件
SIGIOT    實現相關的硬件異常,一般對應SIGABRT
SIGKILL   無法處理和忽略。中止某個進程
SIGLWP    由Solaris Thread Libray內部使用
SIGPIPE   在reader中止之後寫Pipe的時候發送
SIGPOLL   當某個事件發送給Pollable Device的時候發送
SIGPROF   Setitimer指定的Profiling Interval Timer所產生
SIGPWR    和系統相關。和UPS相關。
SIGQUIT   輸入Quit Key的時候(CTRL+/)發送給所有Foreground Group的進程
SIGSEGV   非法內存訪問
SIGSTKFLT Linux專用,數學協處理器的棧異常
SIGSTOP   中止進程。無法處理和忽略。
SIGSYS    非法系統調用
SIGTERM   請求中止進程,kill命令缺省發送
SIGTHAW   Solaris專用,從Suspend恢復時候發送
SIGTRAP   實現相關的硬件異常。一般是調試異常
SIGTSTP   Suspend Key,一般是Ctrl+Z。發送給所有Foreground Group的進程
SIGTTIN   當Background Group的進程嘗試讀取Terminal的時候發送
SIGTTOU   當Background Group的進程嘗試寫Terminal的時候發送
SIGURG    當out-of-band data接收的時候可能發送
SIGUSR1   用戶自定義signal 1
SIGUSR2   用戶自定義signal 2
SIGVTALRM setitimer函數設置的Virtual Interval Timer超時的時候
SIGWAITING Solaris Thread Library內部實現專用
SIGWINCH  當Terminal的窗口大小改變的時候,發送給Foreground Group的所有進程
SIGXCPU   當CPU時間限制超時的時候
SIGXFSZ   進程超過文件大小限制
SIGXRES   Solaris專用,進程超過資源限制的時候發送
三 信號相關的函數
1 signal函數
void (*signal(int sig, void (*func)(int)))(int);
signal是一個帶有sig和func兩個參數的函數,func是一個類型爲void (*)(int)的函數指針。該函數返回一個與func相同類型的指針,指向先前指定信號處理函數的函數指針。準備捕獲的信號的參數由sig給出,接收到的指定信號後要調用的函數由參數func給出。其實這個函數的使用是相當簡單的,通過下面的例子就可以知道。注意信號處理函數的原型必須爲void func(int),或者是下面的特殊值:
    SIG_IGN:忽略信號
    SIG_DFL:恢復信號的默認行爲
這個函數常常可以用下面要將的sigaction函數替代。

2 sigaction
int sigaction(int sig, const struct sigaction *act, struct sigaction *oact);
該函數與signal函數一樣,用於設置與信號sig關聯的動作,而oact如果不是空指針的話,就用它來保存原先對該信號的動作的位置,act則用於設置指定信號的動作。

參數結構體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);
};

信號集sigset_t結構體:
typedef struct
{
    unsigned long sig[_NSIG_WORDS];
}sigset_t;

信號處理函數可以採用void (*sa_handler)(int)或void (*sa_sigaction)(int, siginfo_t *, void *)。到底採用哪個要看sa_flags中是否設置了SA_SIGINFO位,如果設置了就採用void (*sa_sigaction)(int, siginfo_t *, void *),此時可以向處理函數發送附加信息;默認情況下采用void (*sa_handler)(int),此時只能向處理函數發送信號的數值。
sa_handler    此參數和signal()的參數handler相同,代表新的信號處理函數,其他意義請參考signal()。
sa_mask       用來設置在處理該信號時暫時將sa_mask指定的信號集擱置。
sa_restorer   此參數沒有使用。
sa_flags      用來設置信號處理的其他相關操作,下列的數值可用:
sa_flags      還可以設置其他標誌:
SA_RESETHAND  當調用信號處理函數時,將信號的處理函數重置爲缺省值SIG_DFL
SA_RESTART    如果信號中斷了進程的某個系統調用,則系統自動啓動該系統調用
SA_NODEFER    一般情況下, 當信號處理函數運行時,內核將阻塞該給定信號。但是如果設置了 SA_NODEFER標記, 那麼在該信號處理函數運行時,內核將不會阻塞該信號。

返回值
執行成功則返回0,如果有錯誤則返回-1。
錯誤代碼
EINVAL 參數signum 不合法, 或是企圖攔截SIGKILL/SIGSTOPSIGKILL信號
EFAULT 參數act,oldact指針地址無法存取。
EINTR  此調用被中斷

下面一個簡單的例子,程序裏面的sigemptyset、kill函數會在後面講
action.c實例代碼:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>

void sig_handler(int sig)
{
    switch(sig)
    {
    case 23:
         printf("child : the signo is 23, hehe\n");
         return;
    case 22:
         printf("father : hello wordl 22!\n");
         return;
    }
}

int main()
{
    struct sigaction act, oact;
    int    status;

    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    act.sa_handler   = sig_handler;
    sigaction(23, &act, &oact);
    sigaction(22, &act, &oact);

    pid_t pid, ppid;

    if (!(pid = fork()))
    {
        printf("child begin\n");

        kill(getppid(), 22);
        sleep(3);

        printf("child over\n");
    }
    else
    {
        printf("father begin\n");

        kill(getpid(), 23);

        wait(&status);

        if (WIFSIGNALED(status))
        {
            printf("child process receive siganl %d\n", WTERMSIG(status));
        }

        printf("father over\n");
    }

    return 0;
}

3 sigaddset sigdelset
int sigaddset(sigset_t *set, int signum);
sigaddset()用來將參數signum 代表的信號加入至參數set 信號集裏。
返回值
執行成功則返回0,如果有錯誤則返回-1。
錯誤代碼
EFAULT 參數set指針地址無法存取
EINVAL 參數signum非合法的信號編號

int sigdelset(sigset_t *set, int signum)
sigdelset()用來將參數signum代表的信號從參數set信號集裏刪除。
返回值
執行成功則返回0
如果有錯誤則返回-1。
錯誤代碼
EFAULT 參數set指針地址無法存取
EINVAL 參數signum非合法的信號編號

4 sigemptyset sigfillset
int sigemptyset(sigset_t *set);
sigemptyset()用來將參數set信號集初始化並清空。
返回值
執行成功返回0;
失敗返回-1。
錯誤代碼
EFAULT 參數set指針地址無法存取

int sigfillset(sigset_t * set);
sigfillset()用來將參數set信號集初始化,然後把所有的信號加入到此信號集裏。
返回值
執行成功返回0;
失敗返回-1。
錯誤代碼
EFAULT 參數set指針地址無法存取

5 sigismember
int sigismember(const sigset_t *set,int signum);
sigismember()用來測試參數signum 代表的信號是否已加入至參數set信號集裏。如果信號集裏已有該信號則返回1,否則返回0。
返回值
信號集已有該信號則返回1;
沒有則返回0;
如果有錯誤則返回-1。
錯誤代碼
EFAULT 參數set指針地址無法存取
EINVAL 參數signum 非合法的信號編號

6 sigpending
int sigpending(sigset_t *set);
sigpending()會將被擱置的信號集合由參數set指針返回
返回值執
行成功則返回0,
如果有錯誤則返回-1。
錯誤代碼
EFAULT 參數set指針地址無法存取
EINTR  此調用被中斷。

7 sigprocmask
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
一個進程的信號屏蔽字規定了當前阻塞而不能傳送給該進程的信號集。sigprocmask()可以用來檢測或改變目前的信號屏蔽字,其操作參數how來決定,如果參數oldset不是NULL指針,那麼當前的信號屏蔽字會由此指針返回。如果set是一個非空指針,則參數how指示如何修改當前信號屏蔽字。每個進程都有一個用來描述哪些信號遞送到進程時將被阻塞的信號集,該信號集中的所有信號在傳送到進程後都將被阻塞。
參數how:
SIG_BLOCK:該進程新的信號屏蔽字是其當前信號屏蔽字和set指向信號集的並集,set包含了我們希望阻塞的附件信號。
SIG_UNBLOCK:該進程新的信號集屏蔽字是其當前信號屏蔽字和set所指向信號集的補集的交集。set包含了我們希望解除阻塞的信號。
SIG_SETMASK:該進程新的信號屏蔽是set指向的值。
注意:
1)除SIG_SETMASK外,如果set是個空指針,則不改變該進程的信號屏蔽字,這時how的值也沒有意義。
2)SIG_SETMASK與set空指針結合使用,即清空所有屏蔽的信號。
返回值:
執行成功返回0;
失敗返回-1。
錯誤碼
EFAULT:參數set、oldsset指針地址無法獲取
EINTR:此調用被中斷

下面是一個測試例子,測試被屏蔽的信號:
sigprocmask.c的實例代碼:

http://blog.csdn.net/xiaoliangsky/article/details/40264151

////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

void sig_handler(int signum)
{
	switch(signum)
    {
    case SIGINT:
         printf("the signo is SIGINT %d received, hehe\n", SIGINT);
         return;
    case SIGCHLD:
         printf("the signo is SIGCHLD %d received, hihi!\n", SIGCHLD);
         return;
	case SIGUSR1:
         printf("the signo is SIGUSR1 %d received, hihi!\n", SIGUSR1);
         return;
	case SIGIO:
         printf("the signo is SIGIO %d received, hihi!\n", SIGIO);
         return;
	case SIGALRM:
		 printf("the signo is SIGALRM %d received, hihi!\n", SIGALRM);
         return;
	default:
		 printf("the signo %d is not processed\n", signum);
		 return;
    }
}

int main()
{
	int              i;
	int              status;
	pid_t            pid;
	sigset_t         set;
	sigset_t         oldset;
	struct sigaction act;
	struct sigaction oldact;

	act.sa_flags   = 0;
	act.sa_handler = sig_handler;
	sigemptyset(&act.sa_mask);
	sigaction(SIGINT, &act, &oldact);
	sigaction(SIGCHLD, &act, &oldact);
	sigaction(SIGUSR1, &act, &oldact);
	sigaction(SIGALRM, &act, &oldact);
	sigaction(SIGIO, &act, &oldact);

	sigemptyset(&set);
	sigaddset(&set, SIGINT);
	sigaddset(&set, SIGUSR1);

	if ((pid = fork()) < 0)
	{
		fprintf(stdout, "fork error\n");
		exit(-1);
	}
	else if (pid > 0)
	{
		if (sigprocmask(SIG_BLOCK, &set, &oldset) < 0)//屏蔽信SINGINT、SIGUSR1
		{
			fprintf(stdout, "sigprocmask error\n");
			kill(pid, SIGKILL);
			exit(-1);
		}
/*
		waitpid(pid, &status, 0);

		if (WIFSIGNALED(status))
        {
            printf("child process receive siganl %d\n", WTERMSIG(status));
        }
*/
		pause();//接收SIGIO?
		pause();//接收SIGALRM?
		pause();//接收SIGCHLD?
		//pause();
		//pause();
	}
	else
	{
		sleep(1);
		kill(getppid(), SIGINT);//信號被屏蔽
		sleep(1);
		kill(getppid(), SIGIO);
		sleep(1);
		kill(getppid(), SIGUSR1);//信號被屏蔽
		sleep(1);
		kill(getppid(), SIGALRM);
		sleep(2);
		//子進程退出會發送一個SIGCHLD信號
	}
	
	return 0;
}



8 sigsuspend
int sigsuspend(const sigset_t *mask);
函數sigsuspend將進程的信號屏蔽碼設置爲mask,然後與pause()函數一樣等待信號的發生並執行完信號處理函數。信號處理函數執行完後再把進程的信號屏蔽碼設置爲原來的屏蔽字,然後sigsuspend函數才返回。
返回值
sigsuspend總是返回-1
下面是一個sigsuspend的例子:
sigsuspend的實例代碼:

#include <unistd.h>
#include <signal.h>
#include <stdio.h>

void sig_handler(int sig)
{
    if (sig == SIGINT) 
		printf("SIGINT sig\n");
    else if (sig == SIGQUIT)
		printf("SIGQUIT sig\n");
    else if (sig == SIGUSR1)
		printf("SIGUSR1 sig\n");
    else if (sig == SIGUSR2)
		printf("SIGUSR2 sig\n");
    else 
		printf("SIGCHLD sig\n");
}

int main()
{
    int                i;
    sigset_t           new;
    sigset_t           old;
    sigset_t           wait;
    struct sigaction   act;

    act.sa_handler = sig_handler;
    act.sa_flags   = 0;

    sigemptyset(&act.sa_mask);
    sigaction(SIGINT, &act, 0);
    sigaction(SIGQUIT, &act, 0);
    sigaction(SIGUSR1, &act, 0);
    sigaction(SIGUSR2, &act, 0);
    sigaction(SIGCHLD, &act, 0);

    sigemptyset(&new);
    sigaddset(&new, SIGINT);

    sigemptyset(&wait);
    sigaddset(&wait, SIGUSR1);
    sigaddset(&wait, SIGUSR2);

    sigprocmask(SIG_BLOCK, &new, &old);

    for (i=0; i < 90; ++i)
    {
        printf("%d\n", i);
	sleep(1);
    }

    sigsuspend(&wait);

    printf("After sigsuspend\n");

    printf("yyyy\n");
    if (-1 == sigprocmask(SIG_SETMASK, &old, NULL)) 
    {
        perror("sigprocmask");
        exit(-1);
    } 
    for (i=0; i < 30; ++i)
    {
	printf("%d\n", i);
	sleep(1);
    }
    printf("xxxxx\n");

    return 0;
}


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