【发布时间】:2022-10-19 22:41:52
【问题描述】:
当向一个既阻塞又忽略它的进程发送信号时,内核仍然将此信号保留在待处理列表中(我的术语在这里)。在这种情况下,内核的行为就像信号只是被阻塞了一样,尽管它也应该被忽略。我无法理解这种行为。下面是一个 C 代码,例如 SIGUSR1(索引为 10):
#define _GNU_SOURCE
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
void handler(int sig)
{
printf("Received signal %d\n", sig);
}
int main(int argc, char *argv[])
{
printf("PID is %ld\n", (long) getpid());
int sig = SIGUSR1;
//creating the sigaction struct and setting the handler
struct sigaction act;
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
if(sigaction(sig, &act, NULL) == -1)
{
printf("Error: sigaction\n");
exit(1);
}
//Blocking the signal
sigset_t blockedSignals;
sigemptyset(&blockedSignals);
sigaddset(&blockedSignals, sig);
printf("Blocking signal %d\n", sig);
if(sigprocmask(SIG_SETMASK, &blockedSignals, NULL) == -1)
{
printf("Error: sigprocmask\n");
exit(1);
}
//Ignoring the signal
act.sa_handler = SIG_IGN;
printf("Ignoring signal %d\n", sig);
if(sigaction(sig, &act, NULL) == -1)
{
printf("Error: sigaction\n");
exit(1);
}
//Sleeping for a while in order to give the user a chance to send the signal to this process
printf("Sleeping for 20 sec. Please send the signal.\n");
sleep(20);
//Unblocking the signal
/*sigemptyset(&blockedSignals);
printf("Unblocking signal %d\n", sig);
if(sigprocmask(SIG_SETMASK, &blockedSignals, NULL) == -1)
{
printf("Error: sigprocmask\n");
exit(1);
}*/
//Let's check the pending list
sigset_t pendingSignals;
sigemptyset(&pendingSignals);
if(sigpending(&pendingSignals) == -1)
{
printf("Error: sigpending\n");
exit(1);
}
if(sigismember(&pendingSignals, sig) == 1)
{
printf("Signal %d is pending.\n", sig);
}
else
{
printf("Signal %d isn't pending.\n", sig);
}
exit(0);
}
SIGUSR1 既被阻止又被忽略。当这个进程休眠时,如果我向它发送一个 SIGUSR1(从 shell:kill -s SIGUSR1 PID),然后检查挂起的列表,我会得到这个打印:
Signal 10 is pending.
如果我取消注释注释的代码块,这会解除对信号的阻塞:
sigemptyset(&blockedSignals);
printf("Unblocking signal %d\n", sig);
if(sigprocmask(SIG_SETMASK, &blockedSignals, NULL) == -1)
{
printf("Error: sigprocmask\n");
exit(1);
}
并重复实验,我看到以下打印:
Signal 10 isn't pending.
就像内核优先考虑“阻塞”而不是“忽略”。 真的是这样吗?
更新:据我了解,当进程忽略一个信号时,这意味着内核不会将它发送给进程。这也意味着它不会将其保留在待处理列表中。例如,如果一个信号仅被进程阻塞,并且存在于挂起列表中,然后我们调用 'sigaction' 以忽略它,内核将从挂起列表中删除该信号。所以问题是,为什么如果我们提前阻塞+忽略,内核会将信号插入到它的挂起列表中?
【问题讨论】:
-
与您的问题无关,但请注意,您不能在任何信号处理程序中安全地调用
printf()。每the (draft) C11 standard, you can't call any standard C functions:“因此,信号处理程序通常不能调用标准库函数。” POSIX 允许calls to async-signal-safe functions only,但printf()不是异步信号安全函数。你在这里逃脱了,因为你的程序很简单。 -
...值得理解的是,“因此”指的是 C 语言规范明确允许任何和所有标准库函数不可重入并访问静态或线程本地对象的事实。 POSIX 指定某些标准库函数实际上是可重入的,和不要访问静态或线程本地对象(可能还有其他属性),因此可以安全地从信号处理程序调用。
标签: c linux-kernel signals