了解信号,我想知道过程信号掩码,阻塞信号集,信号处理程序和阻塞信号之间的细微差别 .
问题涉及(在Debian上):
-
sigprocmask(2)
-
sigsetops(3)相关功能
每个进程都有自己的信号掩码(包含被阻塞信号的long) . 并且可以通过使用* set变量的NULL参数调用sigprocmask(2)来获得信号集,这将导致旧的进程掩码被放入* oldset,不变:
#include <string.h>
#include <signal.h>
void show_signals(const sigset_t exmask)
{
int exsignals[43];
exsignals[0] = SIGABRT;
exsignals[1] = SIGALRM;
exsignals[2] = SIGBUS;
exsignals[3] = SIGCHLD;
exsignals[4] = SIGCONT;
#ifdef SIGEMT
exsignals[5] = SIGEMT;
#else
exsignals[5] = -1;
#endif
exsignals[6] = SIGFPE;
#ifdef SIGFREEZE
exsignals[7] = SIGFREEZE;
#else
exsignals[7] = -1;
#endif
exsignals[8] = SIGHUP;
exsignals[9] = SIGILL;
#ifdef SIGINFO
exsignals[10] = SIGINFO;
#else
exsignals[10] = -1;
#endif
exsignals[11] = SIGINT;
exsignals[12] = SIGIO;
exsignals[13] = SIGIOT;
#ifdef SIGJVM1
exsignals[14] = SIGJVM1;
#else
exsignals[14] = -1;
#endif
#ifdef SIGJVM2
exsignals[15] = SIGJVM2;
#else
exsignals[15] = -1;
#endif
exsignals[16] = SIGKILL;
#ifdef SIGLOST
exsignals[17] = SIGLOST;
#else
exsignals[17] = -1;
#endif
#ifdef SIGLWP
exsignals[18] = SIGLWP;
#else
exsignals[18] = -1;
#endif
exsignals[19] = SIGPIPE;
exsignals[20] = SIGPOLL;
exsignals[21] = SIGPROF;
exsignals[22] = SIGPWR;
exsignals[23] = SIGQUIT;
exsignals[24] = SIGSEGV;
exsignals[25] = SIGSTKFLT;
exsignals[26] = SIGSTOP;
exsignals[27] = SIGSYS;
exsignals[28] = SIGTERM;
#ifdef SIGTHAW
exsignals[29] = SIGTHAW;
#else
exsignals[29] = -1;
#endif
#ifdef SIGTHR
exsignals[30] = SIGTHR;
#else
exsignals[30] = -1;
#endif
exsignals[31] = SIGTRAP;
exsignals[32] = SIGTSTP;
exsignals[33] = SIGTTIN;
exsignals[34] = SIGTTOU;
exsignals[35] = SIGURG;
exsignals[36] = SIGUSR1;
exsignals[37] = SIGUSR2;
exsignals[38] = SIGVTALRM;
#ifdef SIGWAITING
exsignals[39] = SIGWAITING;
#else
exsignals[39] = -1;
#endif
exsignals[40] = SIGWINCH;
exsignals[41] = SIGXCPU;
exsignals[42] = SIGXFSZ;
#ifdef SIGXRES
exsignals[43] = SIGXRES;
#else
exsignals[43] = -1;
#endif
int exsignals_n = 0;
for (;exsignals_n < 43; exsignals_n++) {
if (exsignals[exsignals_n] == -1) continue;
static char *exsignal_name;
exsignal_name = strsignal(exsignals[exsignals_n]);
switch(sigismember(&exmask, exsignals[exsignals_n]))
{
case 0: break;
case 1: printf("YES %s\n", exsignal_name); break;
case -1: printf("could not obtain signal\n"); break;
default: printf("UNEXPECTED for %s return\n", exsignal_name); break;
}
}
}
const sigset_t getmask(void)
{
static sigset_t retmask;
if ((sigprocmask(SIG_SETMASK, NULL, &retmask)) == -1)
printf("could not obtain process signal mask\n");
return retmask;
}
在我的程序开始时,我意识到过程信号掩码,没有阻止任何信号 . 然后我将信号处理程序放入程序中 .
static void sig_abrt(int signo)
{
printf("Caught SIGABRT\n");
}
int main(void)
{
show_signals(getmask());
signal(SIGABRT, sig_abrt);
show_signals(getmask());
return 0;
}
所以现在有一个SIGABRT的信号处理程序,但如果我再次调用sigprocmask(2),如上所述,SIGABRT将不在进程信号掩码中 . 我尝试使用sigismember(3)进行检查,但只有在调用sigaddset(3)或修改信号掩码的其他函数后才会修改过程信号掩码 .
如果我使用sigaddset(3)阻止SIGABRT,信号处理程序sig_abrt在SIGABRT交付时是否会收到调用?这是否意味着信号掩码影响传递的信号?有什么不同?
另外,有没有办法在不使用sigsetops(3)和sigprocmask(2)函数的情况下阻止进程中的信号?
1 回答
好吧,不 . 信号掩码实际上是特定于线程的 . (在多线程程序中,必须使用pthread_sigmask()来操作当前线程的信号掩码;在单线程程序中,可以使用sigprocmask() . )
而且,它不是"a long" . 它的类型为
sigset_t
,可能是数组,结构或联合类型 . 在任何情况下,都应该将其简单地视为无序位集,每个信号一位 .正确 . 无论您是否分配了信号处理程序,都不会影响信号掩码 .
如果所有线程都阻塞SIGABRT,则在信号被解除阻塞(从信号掩码中删除)之前不会传递 . 如果使用sigwait(),sigwaitinfo()或sigtimedwait()消耗信号,则根本不会调用信号处理程序 .
简短摘要:
可以将信号定向到进程组(kill(),带有
pid == 0
或pid == -pgid
),特定进程(pid
)或特定进程中的特定线程(同一进程中的pthread_kill(),通常在Linux中进行tgkill系统调用) .如果信号指向进程组,则该组中的每个进程都会收到信号的“副本” .
信号掩码定义信号是被阻止还是立即传送 .
在每个过程中,每个信号
可以有一个信号处理程序,或
被忽略(
SIG_IGN
"handler"),或具有默认处置(忽略(Ign),使用(Core)或不使用(Term)核心转储终止进程;或者它可以停止(停止)或继续(继续)执行目标线程或进程) . 有关详细信息,请参阅man 7 signal .
如果某些线程(但不是所有线程)阻塞信号,并且信号不是针对特定线程,则内核会将信号定向到其中一个未阻塞信号的线程(随机) .
有两种捕获信号的方法:
使用信号处理程序 . 仅当信号未被阻止时,信号才被传送到信号处理器 . 如果信号被阻止,则信号的传递将被暂停,直到未被阻止(或被下面的其他选项捕获) .
sigwait(),sigwaitinfo()或sigtimedwait() . 这些函数检查是否有任何信号未决,如果是,"catch"它 . 它们检查的信号集由
sigset_t
类型的函数参数定义 .当内核向进程发送/转发信号时,它首先检查进程是否有一个没有阻塞该信号的线程 . 如果有这样的线程,它通过该线程传递它 . (如果信号具有信号处理程序,则在该线程中调用该信号处理程序;否则,效果为由信号处理决定 . )
如果信号被阻止,则内核会使其等待进程 .
如果进程使用指定信号集中的待处理信号调用
sigwait()
,sigwaitinfo()
或sigtimedwait()
,它将接收有关该信号的信息,并捕获该信号 . (它将不再处于挂起状态,并且不会导致调用信号处理程序;它是"consumed" . )如果进程更改其信号掩码,以便挂起信号被解除阻塞,则由内核传递(就像它在那个时间点发送一样) .
不 . (您可以为
sigprocmask()
实现自己的sigsetops()
和系统调用包装,但这就是它 . )这是一个示例程序 example.c ,您可以在单线程进程中用于探索信号处理程序,捕获信号和信号掩码:
例如,使用它编译它
我建议你准备两个终端 . 在一个终端中,使用运行已编译的程序
并观察其输出 . 它会是这样的
无法捕获KILL和STOP信号 . KILL将始终终止进程,STOP将始终停止(“暂停”)该进程 .
如果在该终端中按Ctrl C,内核将向进程发送INT信号 . (这将通过
report_signal()
信号处理程序提供 . )如果在该终端中按Ctrl Z,内核将向进程发送STOP信号 . shell检测到这一点,在作业控制下推送
./example
,并允许您输入新的shell命令 .fg
命令将./example
带回前台,shell向其发送CONT信号,以便./example
将继续执行 .USR1和USR2信号被阻止,因此它们永远不会被传送到
report_signal()
信号处理程序 .HUP和TERM信号也被阻止,但它们由主线程通过
sigwaitinfo()
接收 .程序在收到TERM信号时退出 .