gpt4 book ai didi

c - 过程信号掩码,阻塞信号集和阻塞信号之间的区别?

转载 作者:IT王子 更新时间:2023-10-29 01:12:38 29 4
gpt4 key购买 nike

学习信号后,我想知道过程信号掩码,阻塞的信号集,信号处理程序和阻塞的信号之间的细微差别。

问题涉及(在Debian上):

  • sigprocmask(2)
  • sigsetops(3)相关函数

  • 每个进程都有自己的信号掩码(包含被阻止信号的长掩码)。并且可以通过使用* 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,则在传递SIGABRT时,信号处理程序sig_abrt是否不会收到调用?这是否意味着信号掩码会影响要传送的信号?有什么区别?

    另外,有没有一种方法可以在不使用sigsetops(3)和sigprocmask(2)函数的情况下阻止进程中的信号?

    最佳答案

    Each process has it's [sic] own signal mask (a long which contains the signals being blocked)



    好吧,不。信号掩码实际上是特定于线程的。 (在多线程程序中,必须使用 pthread_sigmask() 来操作当前线程的信号掩码;在单线程程序中,可以使用 sigprocmask() 。)

    而且,它不是“很长”的。它的类型为 sigset_t,它可以是数组,结构或 union 类型。在任何情况下,都应该简单地将其视为无序位集,每个信号一位。

    So now there is a signal handler for SIGABRT, but SIGABRT will not be in the process signal mask.



    正确的。无论您是否分配了信号处理程序,都不会影响信号掩码。

    If I block SIGABRT with sigaddset(3), will the signal handler sig_abrt not receive the call when the SIGABRT is delivered? Does it mean that the signal mask affects which signals are delivered? What is the difference?



    如果您的所有线程都阻止了SIGABRT,则直到两个信号都被解除阻止(从信号掩码中删除)后,它才会被传递。如果使用 sigwait() sigwaitinfo() sigtimedwait() 消耗了信号,则根本不会调用信号处理程序。

    简短摘要:
  • 信号可以定向到进程组(带有kill()pid == 0 pid == -pgid ),特定进程(pid)或特定进程中的特定线程(同一进程中的 pthread_kill() ,通常是Linux中的 tgkill 系统调用)。
  • 如果将信号定向到某个进程组,则该组中的每个进程都会收到该信号的“副本”。
  • 信号掩码定义信号是被阻塞还是被立即传送。
  • 在每个过程中,每个信号
  • 可以有一个信号处理程序,或者
  • 被忽略(SIG_IGN“handler”)或
  • 具有默认配置(忽略(Ign),在(核心)或没有(术语)核心转储的情况下终止进程;或者可以停止(停止)或继续(继续)目标线程或进程的执行)。有关详细信息,请参见 man 7 signal
  • 如果某些但不是全部线程阻塞信号,并且该信号未针对特定线程,则内核会将信号定向到未阻塞信号的线程之一(随机)。
  • 有两种捕获信号的方法:
  • 使用信号处理程序。仅当信号未被阻塞时,信号才会传递到信号处理程序。如果信号被阻止,则信号的传送将被挂起,直到未被阻止(或被下面的其他选项捕获)。
  • sigwait() sigwaitinfo() sigtimedwait() 。这些功能检查是否有任何信号挂起,如果有,则“捕获”。他们检查的信号集由sigset_t类型的功能参数定义。

  • 当内核向进程发送/转发信号时,它首先检查进程是否具有不阻塞该信号的线程。如果有这样的线程,它将通过该线程传递它。 (如果信号具有信号处理程序,则该信号处理程序将在该线程中被调用;否则,效果由信号处置决定。)

    如果信号被阻止,内核将其挂起等待处理。

    如果该进程使用指定信号集中的未决信号调用 sigwait()sigwaitinfo()sigtimedwait(),则它将接收有关该信号的信息,并捕获该信号。 (它将不再挂起,也不会导致信号处理程序被调用;它是“消耗的”。)

    如果该进程更改了其信号掩码,以使未决信号变得不受阻塞,则它由内核传递(就像在该时间点发送的一样)。

    Also, is there a way to block a signal in a process without using the sigsetops(3) and sigprocmask(2) functions?



    不。(您可以实现自己的 sigsetops()sigprocmask()的syscall包装器,仅此而已。)

    这是一个示例程序 example.c ,您可以在单线程进程中使用它来探索信号处理程序,捕获信号和信号掩码:
    #define  _POSIX_C_SOURCE 200809L
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <signal.h>
    #include <stdio.h>
    #include <errno.h>

    /* Async-signal safe write-to-standard error function.
    Keeps errno unchanged. Do not use stderr otherwise!
    */
    static int wrerrpp(const char *ptr, const char *end)
    {
    const int saved_errno = errno;
    ssize_t chars;

    while (ptr < end) {
    chars = write(STDERR_FILENO, ptr, (size_t)(end - ptr));
    if (chars > 0)
    ptr += chars;
    else
    if (chars != -1) {
    errno = saved_errno;
    return EIO;
    } else
    if (errno != EINTR) {
    const int retval = errno;
    errno = saved_errno;
    return retval;
    }
    }

    errno = saved_errno;
    return 0;
    }

    /* Write the supplied string to standard error.
    Async-signal safe. Keeps errno unchanged.
    Do not mix with stderr!
    */
    static int wrerr(const char *ptr)
    {
    if (!ptr)
    return 0;
    else {
    const char *end = ptr;
    /* strlen() is not async-signal safe, so
    find the end of the string the hard way. */
    while (*end)
    end++;
    return wrerrpp(ptr, end);
    }
    }

    /* Write the supplied long to standard error.
    Async-signal safe. Keeps errno unchanged.
    Do not mix with stderr!
    */
    static int wrerrnum(const long value)
    {
    unsigned long u = (value < 0) ? (unsigned long)-value : (unsigned long)value;
    char buf[40];
    char *ptr = buf + sizeof buf;
    char *const end = buf + sizeof buf;

    do {
    *(--ptr) = '0' + (u % 10uL);
    u /= 10uL;
    } while (u > 0uL);

    if (value < 0)
    *(--ptr) = '-';

    return wrerrpp(ptr, end);
    }

    /* Async-signal safe variant of strsignal().
    Only covers a small subset of all signals.
    Returns NULL if the signal name is not known. */
    static const char *signal_name(const int signum)
    {
    switch (signum) {
    case SIGHUP: return "HUP";
    case SIGINT: return "INT";
    case SIGQUIT: return "QUIT";
    case SIGKILL: return "KILL";
    case SIGSEGV: return "SEGV";
    case SIGTERM: return "TERM";
    case SIGUSR1: return "USR1";
    case SIGUSR2: return "USR2";
    case SIGCHLD: return "CHLD";
    case SIGCONT: return "CONT";
    case SIGSTOP: return "STOP";
    default: return NULL;
    }
    }

    /* Signal handler that reports its delivery immediately,
    but does nothing else.
    */
    static void report_signal(int signum, siginfo_t *info, void *ctx)
    {
    const char *sname = signal_name(signum);

    wrerr("report_signal(): Received signal ");
    if (sname)
    wrerr(sname);
    else
    wrerrnum(signum);

    if (info->si_pid) {
    wrerr(" from process ");
    wrerrnum(info->si_pid);
    wrerr(".\n");
    } else
    wrerr(" from kernel or terminal.\n");

    }

    /* Install report_signal() handler.
    */
    static int install_report_signal(const int signum)
    {
    struct sigaction act;

    memset(&act, 0, sizeof act);

    sigemptyset(&act.sa_mask);

    act.sa_sigaction = report_signal;
    act.sa_flags = SA_SIGINFO;

    if (sigaction(signum, &act, NULL) == -1)
    return errno;

    return 0;
    }


    int main(void)
    {
    sigset_t mask;
    siginfo_t info;
    const char *name;
    int signum;

    if (install_report_signal(SIGINT) ||
    install_report_signal(SIGCONT)) {
    const char *errmsg = strerror(errno);
    wrerr("Cannot install signal handlers: ");
    wrerr(errmsg);
    wrerr(".\n");
    return EXIT_FAILURE;
    }

    sigemptyset(&mask);
    sigaddset(&mask, SIGUSR1);
    sigaddset(&mask, SIGUSR2);
    sigaddset(&mask, SIGHUP);
    sigaddset(&mask, SIGTERM);
    sigprocmask(SIG_SETMASK, &mask, NULL);

    printf("Process %ld is ready to receive signals! Run\n", (long)getpid());
    printf("\tkill -USR1 %ld\n", (long)getpid());
    printf("\tkill -USR2 %ld\n", (long)getpid());
    printf("\tkill -HUP %ld\n", (long)getpid());
    printf("\tkill -TERM %ld\n", (long)getpid());
    printf("in another terminal; press Ctrl+C in this terminal; or press Ctrl+Z and run\n");
    printf("\tfg\n");
    printf("in this terminal.\n");
    fflush(stdout);

    /* Almost same as blocked mask, just without SIGUSR1 and SIGUSR2. */
    sigemptyset(&mask);
    sigaddset(&mask, SIGHUP);
    sigaddset(&mask, SIGTERM);

    do {
    do {
    signum = sigwaitinfo(&mask, &info);
    } while (signum == -1 && errno == EINTR);
    if (signum == -1) {
    const char *errmsg = strerror(errno);
    wrerr("sigwaitinfo(): ");
    wrerr(errmsg);
    wrerr(".\n");
    return EXIT_FAILURE;
    }

    name = signal_name(signum);
    if (name)
    printf("main(): Received signal %s from ", name);
    else
    printf("main(): Received signal %d from ", signum);

    if (info.si_pid == 0)
    printf("kernel or terminal.\n");
    else
    printf("process %ld.\n", (long)info.si_pid);
    fflush(stdout);

    } while (signum != SIGTERM);

    return EXIT_SUCCESS;
    }

    使用例如编译它
    gcc -Wall -O2 example.c -o example

    我建议您准备两个终端。在一个终端中,使用
    ./example

    并观察其输出。它会像

    Process 843 is ready to receive signals! Run
        kill -USR1 843
        kill -USR2 843
        kill -HUP 843
        kill -TERM 843
    in another terminal; press Ctrl+C in this terminal; or press Ctrl+Z and run
        fg
    in this terminal.



    无法捕获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信号时退出。

    关于c - 过程信号掩码,阻塞信号集和阻塞信号之间的区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48409070/

    29 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com