目录
了解了sigset_t是一个什么样的类型之后,就可以来操作它了
但是,sigset_t禁止对它直接操作,只能通过os提供的接口操作,也就是下面介绍的这些函数:
操作sigset_t的接口
介绍
下面我们详细介绍两个接口:
sigpending
函数原型
用于获取当前进程的pending信号集
相当于,将os中的数据以set参数的形式,拿给用户
返回值
sigprocmask
函数原型
获取/修改当前进程的阻塞信号集/阻塞屏蔽字
how
- 添加信号 -- 相当于mask|set
- 删除信号 -- 相当于mask~set
- 替换 -- 相当于mask=set
set
表示要修改的信号集合
oldset
- 输出型参数,用于获取修改前的信号集(万一会用到呢)
- 如果不用,一般传NULL
示例代码
我们用一些代码来验证某些概念和行为:
使特定信号被阻塞 -- 观察pending信号集的变化
代码
#include <signal.h> #include <iostream> #include <cstdlib> #include <unistd.h> using namespace std; void showset(sigset_t *s) { for (int i = 1; i <= 31; ++i) { if (sigismember(s, i)) { cout << "1"; } else { cout << "0"; } } cout << endl; } void func(int signum) { cout << "我收到了" << signum << "号信号" << endl; } void test1() { sigset_t s, olds; sigset_t pending; signal(2, func); sigemptyset(&s); sigemptyset(&olds); sigaddset(&s, 2); // 将2号信号添加进去 sigprocmask(SIG_BLOCK, &s, &olds); // 屏蔽2号信号 int count = 0; while (1) { ++count; sigpending(&pending); showset(&pending); sleep(1); if (count == 10) { sigprocmask(SIG_UNBLOCK, &s, &olds); // 取消2号信号的屏蔽 } } }
运行情况
这里我们可以看到pending信号集的变化过程:
- 从最开始什么信号都没有收到
- 到收到键盘键入的ctrl c后,第2位bit变成了1
- 但因为2号信号被阻塞,所以不会被处理
- 当10s过后,进程解除了信号的阻塞,它就调用了我们的自定义处理函数,并且pending中对应bit位变成了0
全部信号被自定义处理
引入
- 如果全部都用自定义处理方法,并且该函数不包括终止进程功能
- 那么我们的进程是否就无法被终止了呢?
- 我们来验证一下:
代码
.sh文件
#!/bin/bash i=1 id=$(pidof code) # 发送信号 while [ $i -le 31 ] do kill -$i $id echo "kill -$i $id" let i++ sleep 1 done
.cpp文件
我们将所有信号的处理方法,都修改成自定义函数:
void func(int signum) { cout << "我收到了" << signum << "号信号" << endl; } void test3() { for (int i = 1; i <= 31; ++i) { signal(i, func); } while (1) { sleep(1); } }
运行情况
- 当我们用shell脚本给当前进程发送所有信号:
- 当发到9号信号时,信号终止了
- 说明9号信号不允许修改方法
跳过9号信号
#!/bin/bash i=1 id=$(pidof code) # 发送信号 while [ $i -le 31 ] do if [ $i -eq 9 ];then let i++ continue fi kill -$i $id echo "kill -$i $id" let i++ sleep 1 done
会发现,19号信号也无法被修改:
跳过9和19号信号
#!/bin/bash i=1 id=$(pidof code) # 发送信号 while [ $i -le 31 ] do if [ $i -eq 9 ];then let i++ continue fi if [ $i -eq 19 ];then let i++ continue fi kill -$i $id echo "kill -$i $id" let i++ sleep 1 done
当我们shell脚本执行完后,进程依然没有退出,
除非我们手动给当前进程发送9号信号 -- SIGKILL:
全部信号被阻塞
引入
- 我们将所有信号都阻塞,也就是说,进程无法处理任何信号
- 是否这个进程就无法被终止了呢?
- 我们来验证一下:
代码
void block_sig(sigset_t* s){ sigfillset(s); sigprocmask(SIG_SETMASK, s, nullptr); //将所有信号阻塞 } void test2(){ sigset_t s,pending; sigemptyset(&s); block_sig(&s); while (1) { sigpending(&pending); showset(&pending); sleep(1); } }
运行情况
- 进程执行后,运行我们的shell脚本,给我们的进程依次发送信号:
- 会发现,当发送9号信号后,进程被终止了:
- 说明9号信号无法被阻塞
跳过9号信号
- 发送19号信号后,该进程被stop:
- 说明19号信号也无法被阻塞
总结
SIGKILL
是上面例子中提到的9号信号
- 从运行情况我们可以知道,9号信号无法被阻塞,无法被修改行为
- 总的来说,它是一种无法被捕获、阻塞或忽略的信号
- 它强制终止进程,不给予进程执行任何清理工作的机会
SIGSTOP
- 在上面的例子中,他和9号信号一样,都不能被阻塞/被修改行为
- 19号对应的是SIGSTOP 信号,作用是暂停(停止)一个进程的执行,也是一种无法被捕获、阻塞或忽略的信号
- SIGSTOP 不会终止进程,而是将其挂起,暂停其执行,直到收到 SIGCONT 信号(18号)为止