【Linux】信号集及信号集操作函数


一、信号集是什么?

对于每个进程, 都有三个信号集, 分别是:

  • block: 阻塞信号集
  • pending: 未决信号集
  • handler: 信号的处理函数

用 sigset_t 用来表示 block, pending 两个表的类型, 作位图使用, 0/1 分别表示无效/有效, 在 block 表中, 0 表示对应信号未收到阻塞信号, 1 表示对应信号收到阻塞信号, 在 pending 表中, 0 表示该进程未收到对应信号, 1 表示该进程收到对应信号, 在 handler 表中, 存在两种可选的宏, 分别是 SIG_DFL, SIG_IGN, 分别表示执行默认处理函数和忽略该信号, 即收到该信号后什么也不做就是处理动作.

当一个进程的 block 表中的对应信号被置为 1, 即表示该信号被阻塞了, 即使该进程收到了对应信号, 只会将其存储在 pending 表中, 并不会处理该信号, 当该信号在 block 表中被置为 0, 即解除对该信号的阻塞时, 此时如果 pending 表中对应的信号为 1, 才会在合适的时候(由内核态到用户态时)执行对应的处理函数.

其中 block, pending 表中的下标就表示相应的信号, handler 中存储的类型为 void sighandler(int signo).

二、信号集操作函数

1. sigemptyset

头文件: #include <signal.h>
函数声明: int sigemptyset(sigset_t *set);

  • 返回值: 成功返回 0, 失败返回 -1.
  • set: 被设置的集合.

功能: 将信号集(block/pending)中的二进制位都置为 0, 可用于初始化.

示例代码:

#include <iostream>
#include <signal.h>
using namespace std;

int main()
{
    sigset_t set;
    sigemptyset(&set);
    for(int i = 1; i <= 31; ++i)
    {
        cout << sigismember(&set, i) ? 1 : 0;
    }
    cout << endl;
    return 0;
}

运行结果:
在这里插入图片描述
可以看到该集合中的二进制位都被置为 0 了.

2. sigfillset

头文件: #include <signal.h>
函数声明: int sigfillset(sigset_t *set);

  • 返回值: 成功返回 0, 失败返回 -1.
  • set: 被设置的集合.

功能: 将信号集(block/pending)中的二进制位都置为 1.

示例代码:

#include <iostream>
#include <signal.h>
using namespace std;

int main()
{
    sigset_t set;
    sigfillset(&set);
    for(int i = 1; i <= 31; ++i)
    {
        cout << sigismember(&set, i) ? 1 : 0;
    }
    cout << endl;
    return 0;
}

运行功能:
在这里插入图片描述
可以看到该集合中的二进制位都被置为 1 了.

3. sigaddset

头文件: #include <signal.h>
函数声明: int sigaddset(sigset_t *set, int signum);

  • 返回值: 成功返回 0, 失败返回 -1.
  • set: 被设置的集合.
  • signum: 设置的信号

功能: 将对应信号设置到信号集(block/pending)中, 对应信号的二进制位置为 1.

示例代码:

#include <iostream>
#include <signal.h>
using namespace std;

int main()
{
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, 2);
    for(int i = 31; i >= 1; --i)
    {
        cout << sigismember(&set, i) ? 1 : 0;
    }
    cout << endl;
    return 0;
}![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/b0fca399c38143b49331eb6613e9223e.png)

运行结果:
在这里插入图片描述
可以看到, 对应的信号被设置进了信号集中.

4. sigdelset

头文件: #include <signal.h>
函数声明: int sigdelset(sigset_t *set, int signum);

  • 返回值: 成功返回 0, 失败返回 -1.
  • set: 被设置的集合.
  • signum: 需要删除的信号.

功能: 将对应信号从信号集(block/pending)中删除, 对应信号的二进制位置为 0.

示例代码:

#include <iostream>
#include <signal.h>
using namespace std;

int main()
{
    sigset_t set;
    sigfillset(&set);
    sigdelset(&set, 2);
    for(int i = 31; i >= 1; --i)
    {
        cout << sigismember(&set, i) ? 1 : 0;
    }
    cout << endl;
    return 0;
}

运行结果:
在这里插入图片描述
可以看到信号集中对应信号的 1 被置为了 0.

5. sigismember

头文件: #include <signal.h>
函数声明: int sigismember(const sigset_t *set, int signum);

  • 返回值: 如果 signum 信号在 set 中为 1, 则返回 1, 为 0, 则返回 0, 调用失败返回 -1.
  • set: 被检测的集合.
  • signum: 被检测的信号.

功能: 检测信号集中是否收到对应信号.

示例代码:

#include <iostream>
#include <signal.h>
using namespace std;

int main()
{
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, 2);
    for(int i = 31; i >= 1; --i)
    {
        cout << sigismember(&set, i) ? 1 : 0;
    }
    cout << endl;
    return 0;
}

运行结果:
在这里插入图片描述
在代码中, 先对信号集进行初始化, 在将 2 号信号设置进信号集中, 此时通过 sigismember 函数遍历判断信号集收到了哪些信号.

6. sigprocmask (仅用于读取或更改block表)

头文件: #include <signal.h>
函数声明: int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

  • 返回值: 成功返回 0, 失败返回 -1.
  • how: 如何更改进程的 block 表.
  • set: 将 set 信号集的内容设置给进程的 block 表.
  • oldset: 输出型参数, 将调用进程老的 block 表中的内容保存到 oldset 中.

功能: 函数 1 - 5 只是对 sigset_t 类型的变量进行操作, 通过 sigprocmask 函数才能将 sigset_t 类型的变量设置进调用进程的 block 表中.

how 参数可选值:

  • SIG_BLOCK: 将参数 set 中的阻塞信号添加到调用进程的 block 表中, 相当于 mask |= set, 追加式添加阻塞信号.
  • SIG_UNBLOCK: 将参数 set 中的阻塞信号从调用进程的 block 表中解除, 相当于 mask = mask & (~set).
  • SIG_SETMASK: 将调用进程的 block 表设置为 set, 相当于 mask = set, 覆盖式添加阻塞信号.

在 Linux 中, how 的三个参数其实也就是宏:

/* Values for the HOW argument to `sigprocmask'.  */
#define	SIG_BLOCK     0		 /* Block signals.  */
#define	SIG_UNBLOCK   1		 /* Unblock signals.  */
#define	SIG_SETMASK   2		 /* Set the set of blocked signals.  */

示例代码:

#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;

int main()
{
    sigset_t set;
    sigset_t old_set;

    sigemptyset(&set);
    sigemptyset(&old_set);

    sigaddset(&set, 2);

    sigprocmask(SIG_SETMASK, &set, &old_set);

    while(1)
    {
        for(int i = 31; i >= 1; --i)
        {
            cout << sigismember(&set, i) ? 1 : 0;
        }
        cout << endl;
        sleep(1);
    }
    
    return 0;
}

运行结果:
在这里插入图片描述
首先将 2 号信号添加到信号集变量 set 中, 通过 SIG_SETMASK 的方式将 set 中设置的阻塞信号添加到调用进程的阻塞信号集中, 在此之前, 先将调用进程原本的阻塞信号集中的内容添加给 old_set, 而后调用进程的阻塞信号集才会被 set 覆盖, 第三个参数也可以置为 nullptr, 表示不接收调用进程原本的阻塞信号集, 通过运行结果可以直观的看出, 在 2 号信号被阻塞后, 我们在终端输入 Ctrl + C 后就无效了.

7. sigpending (仅用于读取pending表)

头文件: #include <signal.h>
函数声明: int sigpending(sigset_t *set);

  • 返回值: 成功返回 0, 失败返回 -1.
  • set: 输出型参数, 将调用进程的 pending 表设置进 set.

功能: 获取调用进程的 pending 表.

示例代码:

#include <iostream>
#include <signal.h>
using namespace std;

int main()
{
    sigset_t set;
    sigpending(&set);
    for(int i = 31; i >= 1; --i)
    {
        cout << sigismember(&set, i);
        fflush(stdout);
    }
    cout << endl;
    return 0;
}

运行结果:
在这里插入图片描述
调用进程没有收到任何信号, 所以 pending 表全为 0.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值