LINUX 信号处理

来源:互联网 发布:巴基斯坦 歼10 知乎 编辑:程序博客网 时间:2024/04/29 06:52

转自:http://zhangcy.anytome.com/20060321121911820418131452020050715/35.xml

 

入门

信号是有相同的或不同的进程向一个进程传递的事件。信号通常用来向一个进程通知异常事件。

术语:

       产生(generate):当导致信号发生的事件出现时,比如硬件异常,就产生一个针对某个进程的信号

       递送(deliver): 当进程对发给它的信号进程处理时,称为改信号被递送。

       未决(pending):产生信号和递送信号之间的时间间隔称为信号未决。

      部署(disposition):进程如何响应信号,进程可以忽略(ignore),执行默认操作或者使用自定义代码处理该信号。

              linux 按照POSIX.1实现了可靠信号语义  (关于可靠信号与不可靠信号请参看AUPE)。linux 还实现了POSIX.4定义的real-time signals. 命令(kill –l) 可以显示Linux下所有信号。标准信号和实时信号的区别如下:

 

     标准信号

 实时信号

 所有信号已经定义

未定义(前3个已被linuxpthreads-使用)

不能排队(递送前无论产生多少个信号,接受进程只收到一个)。

可以排队 /proc/sys/kernel/rtsig-max 显示队列长度)

不能携带其他数据

可以携带其他数据

不能保证时序

保证时序

如果同时有标准信号和实时信号,Linux先处理标准信号

 

 

信号处理编程

最简单的信号处理

 

#include <signal.h>

#include <stdio.h>

static void sig_usr(int);

 

int main()

{

        if(signal(SIGUSR1,sig_usr)==SIG_ERR)

                perror("cant catch sigusr1");

        for(;;)

                pause();

        return 0;

}

 

static void sig_usr(int signo)

{

        if(signo==SIGUSR1)

                printf("recv SIGUSR1/n");

        return ;

}

程序运行后,在shell里用命令(kill –USR1 pid ) 向程序发送信号,程序就会输出“recv SIGUSR1”。

这个程序显示了信号处理的基本过程,首先我们使用signal函数部署了SIGUSR1信号的处理函数,用我们定义的sig_usr代替了默认函数。然后调用pause使该进程挂起,等待该进程捕获信号。当进程收到了我们发送的USR1信号是sig_usr就被调用,pause返回,由于是死循环该进程又挂起。

       这个程序有一点要说明,signal函数是不推荐使用的,因为signalANSI C定义的它对信号的处理的定义非常含糊,完全依赖于操作系统。有的系统(SVR4)实现的不可靠语义,而BSD实现的是可靠语义。Linux内核和libc4,5SVR4相同,而glibc2BSD相同,在我的机器上是可靠语义的,鬼才知道你机器上什么样子。但是因为它简单,所以都拿来入门。

要写程序还是要使用sigset_t相关函数和sigaction,sigprocmask,sigsuspend,sigpending.

       sigset_t 5个相关函数分别是:

       sigemptyset(sigset_t *set); 初始化set指向的信号集,使其清除所有信号

sigfillset(sigset_t *set); 初始化set指向的信号集,使其包含所有信号。

       sigaddset(sigset_t *set ,int signo); 将一个信号添加到现存信号集中

sigdelset(sigset_t *set,int signo); 从现存信号集中删除一个信号

sigismemberset(const sigset_t *set,int signo); 查询一个信号是否在信号集中。

 

sigaction 检查或修改与指定信号相关的处理动作。

sigprocmask 检查或修改与指定信号的屏蔽字

sigsuspend 在一个原子早在中实现恢复信号屏蔽字,然后使进程睡眠。

sigpending 返回对于调用进程被阻塞不能递送和当前未判决的信号集。

 

使用sigaction的程序:

#include <signal.h>

#include <stdio.h>

static void sig_usr(int);

int main()

{

        struct sigaction action;

        sigemptyset(&action.sa_mask);

        action.sa_handler=sig_usr;

        sigaction(SIGUSR1,&action,NULL);

        for(;;)

                pause();

        return 0;

}

static void sig_usr(int signo)

{

        if(signo==SIGUSR1)

                printf("recv SIGUSR1/n");

        return ;

}

sigaction的原型是:

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

为指定的signum设置信号处理程序,act 是新的信号部署,oldact返回原来的信号部署。sigaction定义如下:

struct sigaction {

       void  (*sa_handler)(int);

       sigset_t sa_mask;

       int sa_flags;

       void (*sa_restorer)(void);

}

sa_handler是函数指针,指向信号处理函数,也可以是SIG_DEL(设置成默认动作)或者SIG_IGN(忽略该信号)。sa_mask定义了在指向处理期间应该阻塞的其他信号集合的掩码。

sa_flags修正sa_handler的行为,可以下面几个值之一:

       SA_NOCLDSTOP 进程忽略子进程产生的任何SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU

       SA_ONESHOT 登记的自定义信号处理程序仅执行一次。执行完毕后恢复默认动作。

       SA_RESTART 让可重启的系统调用起作用

       SA_NOMASK 不避免在信号自己的处理程序中接受信号本身。

sa_restorer已经被废弃。

 

 

实时信号编程

// rtsig.c

#include <signal.h>

#include <stdio.h>

static void sig_proc(int,siginfo_t *,void *);

 

int main()

{

        struct sigaction action;

        sigemptyset(&action.sa_mask);

        action.sa_sigaction=sig_proc;

        action.sa_flags=SA_SIGINFO;

        sigaction(SIGRTMIN+5,&action,NULL);

                pause();

        return 0;

}

 

static void sig_proc(int signo,siginfo_t *info,void *p)

{

        if(signo==SIGRTMIN+5)

        {

                int val=info->si_value.sival_int;

                // int val=*((int *)info->si_value.sival_ptr);     will be error 

                printf("recv value %d/n",val);

                printf("sender pid %d/n",info->si_pid);

                printf("signo %d/n",info->si_signo);

        }

        return ;

}

信号处理程序有两点变化,首先信号处理的接口变化了添加了两个参数,重要的是第二个参数siginfo_t (定义参看man sigaction),它包含了很多信息,包括发送消息的PIDUID,信号携带的数据。其次sa_flags设成了SA_SIGINFO,只有这样处理程序才能接收到信号携带的数据。

// sendsig.c

#include <stdio.h>

#include <signal.h>

 

int main()

{

        int pid,val;

        printf("input pid and value :");

        scanf("%d %d",&pid,&val);

        union sigval sigv;

        sigv.sival_int=val; //sigv.sival_ptr=(void *)&val; will be error

        sigqueue(pid,SIGRTMIN+5,sigv);

        printf("pid is %d/n",getpid());

        return 0;

}

这里发送信号改成了sigqueue sigval携带信号的数据,参看(man sigqueue)

程序运行结果:

sendmsg:

input pid and value :24479 232

pid is 24482 

rtsig:

recv value 232

sender pid 24482

signo 39

来源:
原创粉丝点击