【问题标题】:Linux - Why is a blocked and ignored signal pending?Linux - 为什么一个阻塞和忽略的信号挂起?
【发布时间】: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


【解决方案1】:

阻塞和忽略信号是两个独立的事情。

通过将信号的处置设置为SIG_IGN 来忽略信号,指示当信号被传递结果应该是什么都不做。

阻止信号(通过设置包含该信号的信号掩码)具有完全阻止该信号传递的效果。如果它被接收到,那么它将保持挂起状态,直到解除阻塞或进程终止。在实际传递信号之前,信号处理并不重要。所以,

就像内核优先考虑“阻塞”而不是“忽略”。真的是这样吗?

是的。当信号被阻塞时,无法实现忽略信号的效果。

关于问题的更新:

据我了解,当进程忽略一个信号时,这意味着 内核不会将其发送到进程。

不,这是不正确的。 SIG_IGN 是一个信号处置.这就是该过程响应信号所做的事情。如果内核一开始没有发送信号,它就无法响应。

请注意,信号处置的另一个选项是让进程运行自定义信号处理函数。应该更清楚的是,这是进程必须做的事情,而不是内核为它做的事情。

这也意味着它 不会将其保留在待处理列表中。

意味着被忽略的信号永远不会挂起,但是您对语义的理解是不正确的。

所以问题是,为什么如果我们 block+ignore 从前面开始,内核将信号插入到它的待处理 列表?

因为这就是内核对信号所做的事情。您可以将其描述为内核所做的事情全部信号,但那些未被阻止的信号不会保持很长时间。

【讨论】:

  • 作为一个小问题,我认为最好把信号处置写成过程响应一个信号,而不是什么程序做。该程序不包含处理预定义信号配置的代码。该代码在内核的get_signal() 函数中(在“kernel/signal.c”中)。
猜你喜欢
  • 2016-05-24
  • 2016-08-02
  • 1970-01-01
  • 2011-08-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-27
  • 2018-11-22
相关资源
最近更新 更多