信号的响应方式

信号的响应有四种方式,下面来详细分析。

a、信号的屏蔽

屏蔽信号实际上就是暂缓对信号的响应,采用如下函数进行对信号的屏蔽:

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

参数简析:

  • how:操作命令字,比如阻塞、解除阻塞等
  • set:当前要操作的信号集
  • oldset:若为非空,则将原有阻塞信号集保留到该oldset中

注意到,该函数的操作参数不是单个信号,而是信号集(sigset_t),这意味着我们可以同时对多个信号设置阻塞或解除阻塞。

// 信号集操作函数组
#include <signal.h>
int sigemptyset(sigset_t *set);                   // 清空信号集set
int sigfillset(sigset_t *set);                    // 将所有信号加入信号集set中
int sigaddset(sigset_t *set, int signum);         // 将信号signum添加到信号集set中
int sigdelset(sigset_t *set, int signum);         // 将信号signum从信号集set中剔除
int sigismember(const sigset_t *set, int signum); // 测试信号signum是否在信号集set中

另外,how是具体的操作命令字,可以有如下取值:

  • SIG_BLOCK:阻塞set中的信号(原有正在阻塞的信号保持阻塞)。
  • SIG_SETMASK:阻塞set中的信号(原有正在阻塞的信号给清除)。
  • SIG_UNBLOCK:解除set中的信号。

例如,想要对1、2号信号进行阻塞操作,首先要将这两个信号添加到一个信号集中,操作接口如下:

// 将1、2号信号加入信号集
sigset_t sig;
sigemptyset(&sig);
sigaddset(&sig, SIGHUP); // 加入1号信号
sigaddset(&sig, SIGINT); // 加入2号信号

// 阻塞1、2号信号
setprocmask(SIG_SETMASK, &sig, NULL);

b、信号的捕捉

所谓信号的捕捉,实际就是在信号到达之前,给信号关联一个指定的响应函数,让其到达之后自动运行该函数。

给信号指定关联函数的接口是:

#include <signal.h>
void (*signal(int sig, void (*func)(int) )   ) (int);
// 函数指针 函数

该函数接口比较复杂,下面是其返回值和参数详解:

  • 返回值类型:void (*)(int);
  • 返回值含义:返回一个指向原有的与指定信号关联的函数
  • 参数:
    • sig: 指定要关联的信号
    • func:指定要关联的响应函数

注意到,使用这种方式关联的响应函数的接口是固定的,如下所示:

// 标准信号响应函数接口
void func(int sig)
{
    // ...
}

显然,func中的参数sig就是触发该响应函数的信号,以下示例代码展示了如何捕捉信号SIGINT:

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

void func(int sig)
{
    printf("捕获到信号:%d\n", sig);
}

int main(int argc, char **argv)
{
    // 指定信号SIGINT关联函数
    signal(SIGINT, func);

    // 持续响应信号
    while(1)
        pause();

    return 0;
}

提示:

  1. 函数pause会在信号响应结束后退出,为了让程序可以持续响应信号,上述程序将pause函数放在while循环中。
  2. 由于上述代码捕捉了SIGINT,因此按ctrl+c将无法中断程序,此时可以按ctrl+\(触发另一个信号SIGQUIT)来退出程序的无限循环。

c、信号的默认处理

如果程序没有对信号做任何预先准备,那么当信号达到时,则会按照信号的默认规则进行响应,具体默认规则可使用如下命令查阅:

gec@ubuntu:~$ man 7 signal # 会得到类似如下的表格: 

gec@ubuntu:~$ man 7 signal

# 会得到类似如下的表格:
       ...
       Signal     Value     Action   Comment
       ──────────────────────────────────────────────────────────────────────
       SIGHUP        1       Term    Hangup detected on controlling terminal
                                     or death of controlling process
       SIGINT        2       Term    Interrupt from keyboard
       SIGQUIT       3       Core    Quit from keyboard
       SIGILL        4       Core    Illegal Instruction
       SIGABRT       6       Core    Abort signal from abort(3)
       SIGFPE        8       Core    Floating-point exception
       SIGKILL       9       Term    Kill signal
       SIGSEGV      11       Core    Invalid memory reference
       SIGPIPE      13       Term    Broken pipe: write to pipe with no
                                     readers; see pipe(7)
       ...

列表中的 Action 一列就是系统对信号的默认处理规则,m默认规则如下:

  • Term:中断目标进程。
  • Core:中断目标进程,且产生核心转储文件core。
  • Stop:暂停目标进程,直到收到信号SIGCONT
  • Cont:恢复目标进程运行
  • Ign:忽略信号

其中需要说明的是:

  1. Term和Core都是中断程序,但Core处理方式还会产生转储文件core,core文件即程序在被中断的瞬间其内存映像的快照,用来给后续的调试提供追踪信息。但一般情况下系统是禁止生成所谓转储文件的,放开此项限制的命令是:
# 查看当前系统对 core 文件的限制
gec@ubuntu:~$ ulimit -a
core file size          (blocks, -c) 0  # core 文件大小被限制为0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 7635
max locked memory       (kbytes, -l) 16384
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 7635
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
gec@ubuntu:~$ 

# 将 core 文件的大小设置为“不限制”
gec@ubuntu:~$ ulimit -c unlimited

  1. Ign是默认就会被忽略的信号,典型的例子是SIGCHLD,此信号是子进程在状态转变时(比如变成僵尸时)自动发给其父进程的信号。
  2. SIGKILL和SIGSTOP这两个信号只能采取默认处理,不能阻塞、捕捉,也不能忽略。

d、信号的忽略

忽略信号就是直接将收到的信号丢弃,做法如下:

int main()
{
    // 忽略信号SIGINT
    signal(SIGINT, SIG_IGN);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值