信号集
在 SylixOS 中,需要一个能表示多个信号的信号集,以便告诉内核不允许递送该信号集中的信号。
信号集操作
不同的信号编号可能超过一个整型量所包含的位数,所以一般而言,不能用整型量来表示一个信号集。POSIX.1 定义了数据类型 sigset_t 来定义相应的信号集,不同的系统 sigset_t 可能有不同的定义方法,因此不应该假设 sigset_t 应该是什么样的类型,SylixOS 定义了下面五个函数来对信号集进行操作:
#include <signal.h>
int sigemptyset(sigset_t *psigset);
int sigfillset(sigset_t *psigset);
int sigaddset(sigset_t *psigset, int iSigNo);
int sigdelset(sigset_t *psigset, int iSigNo);
int sigismember(const sigset_t *psigset, int iSigNo);
函数 sigemptyset 原型分析:
- 此函数返回 0。
- 参数 psigset 是要操作的信号集。
函数 sigfillset 原型分析:
- 此函数返回 0。
- 参数 psigset 是要操作的信号集。
函数 sigaddset 原型分析:
- 此函数成功时返回 0,失败时返回-1 并设置错误号。
- 参数 psigset 是要添加信号的信号集。
- 参数 iSigNo 是我们添加到信号集的信号。
函数 sigdelset 原型分析:
- 此函数成功返回 0,失败返回-1 并设置相应的错误号。
- 参数 psigset 是要删除信号的信号集。
- 参数 iSigNo 是要删除的信号。
函数 sigismember 原型分析:
- 此函数返回 1 代表属于指定的信号集,0 代表不属于指定的信号集,-1 代表错误并设置错误号。
- 参数 psigset 是要判断的信号集。
- 参数 iSigNo 是被判断的信号。
sigemptyset 函数初始化一个信号集,清除其中所有的信号;sigfillset 函数初始化一个信号集,使其包含所有信号,所有应用程序在操作信号集之前都要调用一次 sigemptyset 函数或者 sigfillset 函数。sigaddset 函数将指定的信号添加到已有的信号集中,注意,已有的信号集进行了初始化;函数 sigdelset 将指定的信号从已有的信号集中删除;函数 sigismember 判断一个信号是否包含在指定的信号集中。
信号屏蔽字
一个线程的信号屏蔽字(或者称作信号掩码)是指当前屏蔽而不能递送给该线程的信号集。调用 sigprocmask 函数可以检测、更改或同时进行检测和更改信号屏蔽字。
#include <signal.h>
int sigprocmask(int iHow,
const sigset_t *sigset,
sigset_t *sigsetOld);
函数 sigprocmask 原型分析:
- 此函数成功时返回 0,失败时返回-1 并设置错误号。
- 参数 iHow 是信号集操作的命令如下表所示:
宏名 | 值 |
---|---|
SIG_BLOCK | 新的信号集(sigset)以或的形式添加到当前信号屏蔽字中 |
SIG_UNBLOCK | 从当前信号屏蔽字中删除新的信号集(sigset)中包含的信号 |
SIG_SETMASK | 将新的信号集(sigset)赋值给当前信号屏蔽字 |
- 参数 sigset 是新的信号集。
- 输出参数 sigsetOld 保存先前的信号集。
如果 sigset 为 NULL,则不改变该线程的信号屏蔽字(特殊地,如果此时 sigsetOld 非空,则返回该线程的当前信号屏蔽字), iHow 的值也没有意义;如果 sigsetOld 是 NULL,则不会保存先前的信号集。
在调用 sigprocmask 函数之后如果有任何未决(pending)的、不再屏蔽的信号,在 sigprocmask 返回前,至少将其中之一递送给该进程。
作为早期 BSD 兼容接口,SylixOS 提供了下面一组函数,对信号屏蔽字进行操作。
#include <signal.h>
int sigmask(int iSigNo);
int siggetmask(VOID);
int sigsetmask(int iMask);
int sigblock(int iBlock);
函数 sigmask 原型分析:
- 此函数成功返回信号掩码,失败返回 0 并设置错误号。
- 参数 iSigNo 是信号值。
函数 siggetmask 原型分析:
- 此函数返回当前线程信号屏蔽字。
函数 sigsetmask 原型分析:
- 此函数返回设置前的信号屏蔽字。
- 参数 iMask 是新的信号屏蔽字。
函数 sigblock 原型分析:
- 此函数返回设置前的信号屏蔽字。
- 参数 iBlock 是新的需要添加的信号集。
sigmask 函数通过信号值来获取此信号对应的屏蔽位(掩码位),调用 siggetmask 函数可以获得当前线程的信号屏蔽字,调用 sigsetmask 函数可将指定的信号集设置为当前线程的信号屏蔽字,调用 sigblock 函数可将指定的信号集以或的方式添加到当前线程的信号屏蔽字,注意,此函数与 sigsetmask 函数不同的是,此函数不会替代先前的信号屏蔽字,而 sigsetmask 函数将用新的信号集替代当前线程的信号屏蔽字。
sigpending 函数返回当前线程未决的信号集,其中的信号是阻塞不能递送的。
#include <signal.h>
int sigpending(sigset_t *sigset);
函数 sigpending 原型分析:
- 此函数成功时返回 0,失败时返回-1 并设置错误号。
- 输出参数 sigset 返回未决的信号集。
使用实例
下面实例展示了信号集函数的使用。程序首先将 SIGALRM 信号添加到线程(进程的主线程)信号屏蔽字中,经过 2 秒产生 SIGALRM 信号,之后调用 sigpending 函数获取线程的未决信号集并判断是否包含 SIGALRM 信号,最后恢复之前的信号屏蔽字。
#include <stdio.h>
#include <signal.h>
void int_handler (int signum)
{
fprintf(stdout, "signal SIGALRM\n");
if (signal(SIGALRM, SIG_DFL) == SIG_ERR) {
fprintf(stderr, "Reset SIGALRM error.\n");
}
}
int main (int argc, char *argv[])
{
sigset_t newmask, oldmask, pendmask;
if (signal(SIGALRM, int_handler) == SIG_ERR) {
fprintf(stderr, "Signal error.\n");
return (-1);
}
sigemptyset(&newmask);
sigaddset(&newmask, SIGALRM);
if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) {
fprintf(stderr, "Sigprocmask error.\n");
return (-1);
}
alarm(2);
sleep(5);
sigpending(&pendmask);
if (sigismember(&pendmask, SIGALRM) == 1) {
fprintf(stdout, "Signal SIGALRM pending.\n");
} else {
fprintf(stdout, "SIGALRM no pending.\n");
}
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) {
fprintf(stderr, "Resume mask error.\n");
return (-1);
}
sleep(5);
return (0);
}
在 SylixOS Shell 下运行程序,结果如下:
#./SignalSet
Signal SIGALRM pending.
Signal SIGALRM
从运行结果可以看出,SIGALRM 信号被屏蔽了,但是当恢复非屏蔽状态时,SIGALRM 信号处理函数得到了执行,执行结果说明信号被屏蔽并没有将信号丢弃,当恢复非屏蔽状态时,信号会继续被递送。