信号是与一定的进程相联系的。也就是说一个进程可以决定在进程中对哪些信号进行什 么样的处理。例如一个进程可以忽略某些信号而只处理其他一些信号另外一个进程还可以选择如何处理信号。总之这些总与特定的进程相联系的。因此首 先要建立其信号和进程的对应关系这就是信号的安装登记。

Linux 主要有两个函数实现信号的安装登记signal和sigaction。其中signal在系统调用的基础上实现是库函数。它只有两个参数不支持信号 传递信息主要是用于前32个非实时信号的安装而sigaction是较新的函数由两个系统调用实现sys_signal以及 sys_rt_sigaction有三个参数支持信号传递信息主要用来与sigqueue系统调用配合使用。当然sigaction同样支持非 实时信号的安装sigaction优于signal主要体现在支持信号带有参数。

对 于应用程序自行处理的信号来说信号的生命周期要经过信号的安装登记、信号集操作、信号的发送和信号的处理四个阶段。信号的安装登记指的是在应用程序中 安装对此信号的处理方法。信号集操作的作用是用于对指定的一个或多个信号进行信号屏蔽此阶段对有些应用程序来说并不需要。信号的发送指的是发送信号可 以通过硬件如在终端上按下Ctrl-C发送的信号和软件如通过kill函数发送的信号。信号的处理指的是操作系统对接收信号进程的处理处理方法 是先检查信号集操作函数是否对此信号进行屏蔽如果没有屏蔽操作系统将按信号安装函数中登记注册的处理函数完成对此进程的处理。

1.   signal函数

1函数说明

在signal函数中有两个形参分别代表需要处理的信号编号值和处理信号函数的指针。它主要是用于前32种非实时信号的处理不支持信号的传递信息。但是由于使用简单易于理解因此在许多场合被程序员使用。

对 于Unix系统来说使用signal函数时自定义处理信号函数执行一次后失效对该信号的处理回到默认处理方式。下面以一个例子进行说明例如一程序 中使用signal(SIGQUIT, my_func)函数调用其中my_func是自定义函数。应用进程收到SIGQUIT信号时会跳转到自定义处理信号函数my_func处执行执行 后信号注册函数my_func失效对SIGQUIT信号的处理回到操作系统的默认处理方式当应用进程再次收到SIGQUIT信号时会按操作系统默认 的处理方式进行处理即不再执行my_func处理函数。而在Linux系统中signal函数已被改写由sigaction函数封装实现则不存 在上述问题。

 

2signal函数原型

       signal设置信号处理方式

所需头文件

#include <signal.h>

函数说明

设置信号处理方式。signal()会依参数signum指定的信号编号来设置该信号的处理函数。当指定的信号到达时就会跳转到参数handler指定的函数执行

函数原型

void (*signal(int signum,void(* handler)(int)))(int)

函数传入值

 

signum

指定信号编号

handle

SIG_IGN忽略参数signum指定的信号

SIG_DFL将参数signum指定的信号重设为核心预设的信号处理方式即采用系统默认方式处理信号

自定义信号函数处理指针

函数返回值

成功

返回先前的信号处理函数指针

出错

SIG_ERR(-1)

附加说明

在Unix环境中在信号发生跳转到自定的handler处理函数执行后系统会自动将此处理函数换回原来系统预设的处理方式如果要改变此情形请改用sigaction函数。在Linux环境中不存在此问题

 

signal函数原型比较复杂如果使用下面的typedef则可使其简化。

typedef void sign(int)

sign *signal(int, handler *)

可见该函数原型首先整体指向一个无返回值带一个整型参数的函数指针也就是信号的原始配置函数。接着该原型又带有两个参数其中的第二个参数可以是用户自定义的信号处理函数的函数指针。对这个函数格式可以不理解但需要学会模仿使用。

 

3 signal函数使用实例

该示例表明了如何使用signal函数进行安装登记信号处理函数。当该信号发生时登记的信号处理函数会捕捉到相应的信号并做出给定的处理。这里my_func就是信号处理的函数指针。读者还可以将my_func改为SIG_IGN或SIG_DFL查看运行结果。

signal.c源代码如下

#include <signal.h>

#include <stdio.h>

#include <stdlib.h>

/*自定义信号处理函数*/

void my_func(int sign_no)

{

    if(sign_no==SIGINT)

        printf("I have get SIGINT\n");

    else if(sign_no==SIGQUIT)

        printf("I have get SIGQUIT\n");

}

int main()

{

    printf("Waiting for signal SIGINT or SIGQUIT \n ");

    /*发出相应的信号并跳转到信号处理函数处*/

    signal(SIGINT, my_func);

    signal(SIGQUIT, my_func);

    pause();

pause();

    exit(0);

}

编译 gcc signal.c –o signal。

执行 ./signal执行结果如下

Waiting for signal SIGINT or SIGQUIT

I have get SIGINT       /*按下Ctrl+C,操作系统就会向进程发送SIGINT信号*/

I have get SIGQUIT      /*按下Ctrl-\退出,操作系统就会向进程发送SIGQUIT信号*/

2.   sigaction函数

1sigaction函数原型

sigaction函数用来查询和设置信号处理方式它是用来替换早期的signal函数。sigaction函数原型及说明如下

             sigaction查询和设置信号处理方式

 

 

所需头文件

#include <signal.h>

函数说明

sigaction()会依参数signum指定的信号编号来设置该信号的处理函数

函数原型

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

函数传入值

 

signum

可以指定SIGKILL和SIGSTOP以外的所有信号

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);

}

①    sa_handler此参数和signal()的参数handler相同此参数主要用来对信号旧的安装函数signal()处理形式的支持

②    sa_sigaction新的信号安装机制处理函数被调用的时候不但可以得到信号编号而且可以获悉被调用的原因以及产生问题的上下文的相关信息。

③    sa_mask用来设置在处理该信号时暂时将sa_mask指定的信号搁置

④    sa_restorer 此参数没有使用

⑤    sa_flags用来设置信号处理的其他相关操作下列的数值可用。可用OR 运算|组合

   A_NOCLDSTOP:如果参数signum为SIGCHLD则当子进程暂停时并不会通知父进程

   SA_ONESHOT/SA_RESETHAND:当调用新的信号处理函数前将此信号处理方式改为系统预设的方式

   SA_RESTART:被信号中断的系统调用会自行重启

   SA_NOMASK/SA_NODEFER:在处理此信号未结束前不理会此信号的再次到来

   SA_SIGINFO信号处理函数是带有三个参数的sa_sigaction

oldact

如果参数oldact不是NULL指针则原来的信号处理方式会由此结构sigaction返回

函数返回值

成功0

出错-1错误原因存于error中

附加说明

信号处理安装的新旧两种机制

①     使用旧的处理机制struct sigaction act;  act.sa_handler=handler_old;

②     使用新的处理机制struct sigaction act; act.sa_sigaction=handler_new;

并设置sa_flags的SA_SIGINFO位

错误代码

EINVAL参数signum不合法或是企图拦截SIGKILL/SIGSTOP信号

EFAULT参数actoldact指针地址无法存取

EINTR此调用被中断

 

2sigaction函数使用实例

sigaction.c源代码如下

#include <stdio.h>

#include <stdlib.h>

#include <signal.h> 

#include <sys/types.h> 

#include <unistd.h> 

void new_op(int, siginfo_t *, void *); 

int main(int argc,char**argv) 

    struct sigaction act; 

    int sig; 

    sig=atoi(argv[1]); 

    sigemptyset(&act.sa_mask); 

    act.sa_flags=SA_SIGINFO; 

    act.sa_sigaction=new_op; 

    if(sigaction(sig,&act,NULL) < 0) 

    { 

        perror("install sigal error"); 

        return -1 ;

    } 

    while(1) 

    { 

        sleep(2); 

        printf("wait for the signal\n"); 

    } 

 

    return 0 ;

void new_op(int signum,siginfo_t *info,void *myact) 

    printf("receive signal %d\n", signum); 

    sleep(5); 

编译 gcc sigaction.c -o sigaction。

执行 ./sigaction 2执行结果如下

wait for the signal

receive signal 2       /*按下Ctrl+C */

退出                   /*按下Ctrl-\ */

3.   信号集操作函数

由 于有时需要把多个信号当作一个集合进行处理这样信号集就产生了信号集用来描述一类信号的集合Linux所支持的信号可以全部或部分的出现在信号集 中。信号集操作函数最常用的地方就是用于信号屏蔽。比如有时候希望某个进程正确执行而不想进程受到一些信号的影响此时就需要用到信号集操作函数完成对 这些信号的屏蔽。

    信号集操作函数按照功能和使用顺序分为三类分别为创建信号集函数设置信号屏蔽位函数和查询被搁置未决的信号函数。创建信号集函数只是创建一个信号 的集合设置信号屏蔽位函数对指定信号集中的信号进行屏蔽查询被搁置的信号函数是用来查询当前“未决”的信号集。信号集函数组并不能完成信号的安装登记 工作信号的安装登记需要通过sigaction函数或signal函数来完成。

查 询被搁置的信号是信号处理的后续步骤但不是必需的。由于有时进程在某时间段内要求阻塞一些信号程序完成特定工作后解除对该信号阻塞这个时间段内被阻 塞的信号称为“未决”信号。这些信号已经产生但没有被处理sigpending函数用来检测进程的这些“未决”信号并进一步决定对它们做何种处理 包括不处理。

 

1    创建信号集函数

    创建信号集函数有如下5个

①    sigemptyset初始化信号集合为空。

②    sigfillset把所有信号加入到集合中信号集中将包含Linux支持的64种信号。

③    sigaddset将指定信号加入到信号集合中去。

④    sigdelset将指定信号从信号集中删去。

⑤    sigismember查询指定信号是否在信号集合之中。

 

             创建信号集合函数原型

所需头文件

#include <signal.h>

函数原型

int sigemptyset(sigset_t *set)

int sigfillset(sigset_t *set)

int sigaddset(sigset_t *set,int signum)

int sigdelset(sigset_t *set,int signum)

int sigismember(sigset_t *set,int signum)

函数传入值

set信号集

signum指定信号值

函数返回值

成功0sigismember函数例外成功返回1失败返回 0

出错-1错误原因存于error中

 

2    设置信号屏蔽位函数

每个进程都有一个用来描述哪些信号递送到进程时将被阻塞的信号集该信号集中的所有信号在递送到进程后都将被阻塞。调用函数sigprocmask可设定信号集内的信号阻塞或不阻塞。其函数原型及说明如下

             sigprocmask设置信号屏蔽位

 

 

所需头文件

#include <signal.h>

函数原型

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

函数传入值

how(决定函数的操作方式)

SIG_BLOCK增加一个信号集合到当前进程的阻塞集合之中

SIG_UNBLOCK从当前的阻塞集合之中***一个信号集合

SIG_SETMASK将当前的信号集合设置为信号阻塞集合

set指定信号集

oset信号屏蔽字

函数返回值

成功0

出错-1错误原因存于error中

 

3    查询被搁置未决信号函数

sigpending函数用来查询“未决”信号。其函数原型及说明如下

              sigpending查询未决信号

所需头文件

#include <signal.h>

函数说明

将被搁置的信号集合由参数set指针返回

函数原型

int sigpending(sigset_t *set)

函数传入值

set要检测信号集

函数返回值

成功0

出错-1错误原因存于error中

错误代码

EFAULT参数set指针地址无法存取

EINTR此调用被中断

 

4    对信号集操作函数的使用方法

    对信号集操作函数的使用方法和顺序如下

①      使用signal或sigaction函数安装和登记信号的处理。

②      使用sigemptyset等定义信号集函数完成对信号集的定义。

③      使用sigprocmask函数设置信号屏蔽位。

④      使用sigpending函数检测未决信号非必需步骤。

 

5    信号集操作函数使用实例

该 实例首先使用sigaction函数对SIGINT信号进行安装登记安装登记使用了新旧两种机制其中#if 0进行注释掉的部分为信号安装的新机制。接着程序把SIGQUIT、SIGINT两个信号加入信号集并把该信号集设为阻塞状态。程序开始睡眠30秒此 时用户按下Ctrl+C程序将测试到此未决信号SIGINT随后程序再睡眠30秒后对SIGINT信号解除阻塞此时将处理SIGINT登记的信 号函数my_func。最后可以用SIGQUITCtrl+\信号结束进程执行。

sigset.c源代码如下

#include <sys/types.h>

#include <unistd.h>

#include <signal.h>

#include <stdio.h>

#include <stdlib.h>

/*自定义的信号处理函数*/

#if 0

void my_funcnew(int signum, siginfo_t *info,void *myact)

#endif

void my_func(int signum)

{

    printf("If you want to quit,please try SIGQUIT\n");

}

int main()

{

    sigset_t set, pendset;

    struct sigaction action1,action2;

 

    /*设置信号处理方式*/

    sigemptyset(&action1.sa_mask);

 

#if 0 /*信号新的安装机制*/

    action1.sa_flags= SA_SIGINFO;

    action1.sa_sigaction=my_funcnew;

#endif

    /*信号旧的安装机制*/

    action1.sa_flags= 0;

    action1.sa_handler=my_func;

    sigaction(SIGINT,&action1,NULL);

 

    /*初始化信号集为空*/

    if(sigemptyset(&set)<0)

    {

        perror("sigemptyset");

        return -1 ;

    }

    /*将相应的信号加入信号集*/

    if(sigaddset(&set,SIGQUIT)<0)

    {

        perror("sigaddset");

        return -1 ;

    }

    if(sigaddset(&set,SIGINT)<0)

    {

        perror("sigaddset");

        return -1 ;

    }

 

    /*设置信号集屏蔽字*/

    if(sigprocmask(SIG_BLOCK,&set,NULL)<0)

    {

        perror("sigprocmask");

        return -1 ;

    }

    else

    {

        printf("blocked\n");

    }

 

    /*测试信号是否加入该信号集*/

    if(sigismember(&set,SIGINT)){

            printf("SIGINT in set\n") ;

    }

 

    sleep( 30 ) ;

    /*测试未决信号*/

    if ( sigpending(&pendset) <0 )

    {

        perror("get pending mask error");

    }

    if(sigismember(&pendset, SIGINT) )

    {

        printf("signal SIGINT is pending\n");

    }

 

    sleep(30) ;

    if(sigprocmask(SIG_UNBLOCK,&set,NULL)<0)

    {

        perror("sigprocmask");

        return -1 ;

    }

    else

        printf("unblock\n");

 

    while(1)

    {

        sleep(1) ;

    }

 

    return 0 ;

}                                             

编译 gcc sigset.c -o sigset。

执行 ./sigset执行结果如下

blocked

SIGINT in set   /*按下Ctrl+C */

signal SIGINT is pending

If you want to quit,please try SIGQUIT  /*按下Ctrl+C */

退出