Unix信号处理

  Unix中每一个信号对应一个信号编号,该编号写在文件<signal.h>中。Unix共有五种方式可以产生信号:

  • 由终端发送信号至前台进程组。
  • 出现硬件异常,产生异常信号
  • 出现软件异常,产生异常信号
  • 由kill命令发送相应信号至指定进程
  • 当前进程调用kill函数发送指定信号至指定进程

  对信号的处理过程分为三种情况

  • 忽略该信号
  • 捕获信号,并执行相应信号处理程序
  • 由系统执行默认信号处理操作

需注意的是,SIGSTOP和SIGKILL信号是不可以忽略或捕获的。

 

  使用signal或sigaction函数可以为信号指定其信号处理函数。函数原型如下

#include <signal.h>
void  (*signal(int signo, void (*fun)(int)))(int);
                //为信号指定其信号处理函数,返回值为函数调用前的信号处理函数。
                //当设置失败时,函数返回SIG_ERR
int   sigaction(int signo, const struct sigaction * act, struct sigaction * oact); 
                //为信号指定信号处理方式

 

signal函数中,第二个参数可以为SIG_DEF、SIG_IGN或用户自定义的信号处理函数。

结构体sigaction定义如下

struct sigaction{
void       (*sa_handler)(int);    //信号处理函数

sigset_t    sa_mask;            //信号掩码
int       sa_flags;           //信号操作

void      (*sa_sigaction)(int, siginfo_t *, void*);  //信号上下文信息
};

 

  结构体中sa_mask域指明信号处理函数中将要阻塞的信号,sa_flags域指明了信号的操作方式,如图1所示

图1.  信号处理标志选项

  当sa_flags域为SA_SIGINFO时,sa_sigaction域将代替sa_handler域作为信号处理函数使用。

  在部分Unix操作系统中,使用了不可靠信号(unreliable signals),此时,signal函数指定的信号处理函数仅会被确保正确一次,调用结束后,信号处理函数恢复为默认。信号从产生到发送的过程中,信号可以被阻塞。同种信号只能阻塞一个。当阻塞消失时,该信号会被发送至相应进程。每一个进程都有一个信号掩码来标明哪些信号会被阻塞,哪些信号不会产生阻塞。信号掩码保存在信号集中,用户可以通过一系列函数控制信号集,来达到控制信号阻塞的目的。函数原型如下:

#include <signal.h>

int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
                  //正常退出时返回0,错误时返回-1
int sigismember(const sigset_t *set, int signo);  
                  //如果signo在信号集中,返回1;如果signo不在信号集中,返回0;错误时返回-1
int sigprocmask(int how, const sigset_t *set, sigset_t *restrict oset);  
                  //正常退出时返回0,错误时返回-1

 

  sigprocmask函数可以对当前进程信号集进行修改。若oset参数不为空,则函数调用前的进程信号集被保存在参数oset中。当参数how为SIG_BLOCK时,信号集set中的信号将被阻塞;当参数how为SIG_UNBLOCK时,信号集set中的信号将被停止阻塞;当参数how为SIG_SETMASK时,当前进程信号集将被替换为set参数指定的信号集。如果信号集参数set为空,则当前进程信号集不变。

  如果在部分信号被阻塞时,使用了longjmp函数进行跳转,可能导致部分信号被锁死。这时可以使用sigsetjmp、siglongjmp函数对来代替setjmp、longjmp函数对。两对函数之间唯一的区别在于,当sigsetjmp函数肿savemask参数不为0时,env变量将保存当前进程掩码,并在调用siglongjmp函数后恢复。

  当前进程可以通过调用kill函数或raise函数为指定进程发送信号。其函数原型为

int kill(pid_t pid, int signo);  
int raise(int signo);        //仅能向自己发送信号

 

  若当前进程具有超级用户权限,kill函数可以向任意进程发送信号。若当前进程不具有超级用户权限,kill函数仅能向具有相同用户名或相同有效用户名的进程发送信号。如果发送的信号是SIGCONT,则无论当前进程权限如何,该信号都可以发送至处于同一会话(session)下的所有进程,并且该信号不会被阻塞。当pid参数为-1时,kill函数将发送信号至系统中所有能够接收当前进程所发送信号的进程。

  当产生的信号中断了部分慢系统调用("slow" system call)时,系统调用会被迫返回一个错误,并设置errno为EINTR。这里的慢系统调用往往指

  • 打开文件,或从确定文件中读、写
  • pause函数或wait函数调用
  • 确定的ioctl操作
  • 一些进程间通信函数

  这里的文件指管道、终端设备、网络设备等确定文件(certain file tyep)。在Linux操作系统中,会默认重新启动被信号中断的系统调用。当信号中断了部分函数调用时,部分被中断的函数可以被重新进入而不改变原函数中保存的数据。能够重新进入的函数列表如图2所示

图2.  当被信号中断时能够重新进入的函数

  此外,alarm函数可以为当前进程设定闹钟时间,当闹钟时间到达时,当前进程将产生SIGALRM信号。

  pause函数可以使当前进程暂停,直到接收到一个信号为止,并总是返回-1。sigsuspend函数可以使当前进程暂停,直到接收到非阻塞信号为止,并总是返回-1。sigsuspend函数与pause函数的区别在于,sigsuspend函数可以指定一个信号集,信号集中的信号将在进程暂停期间被阻塞,仅当非信号集中的信号被送达时,sigsuspend函数才返回。

  sleep函数可以使当前进程休眠指定时间,若在休眠过程中接收到信号,则sleep函数返回,返回值为剩余休眠时间。

  abort函数可以产生信号SIGABORT,使当前进程被迫停止。若用户自己捕获该信号,则进程将在信号处理函数结束后停止。用户可以在信号处理函数中对当前进程执行扫尾工作。

  psignal函数类似于错误处理中的perror函数,将在标准错误输出中输出指定消息和信号描述信息。strsignal函数将返回指定信号的信号描述信息。

  sig2str和str2sig函数能够在信号和信号名之间自由转换。此处的信号名不包含前缀"SIG"。

转载于:https://www.cnblogs.com/pgx1030/archive/2012/08/25/2656685.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值