如何在C中使用sigwaitinfo()

时间:2018-12-20 22:14:35

标签: c signals

我正在尝试等待儿子的信号。它似乎不起作用。我不太喜欢C,所以我的代码可能很糟糕。这是代码:

父母代码:

sigemptyset (&mask);
sigaddset (&mask, SIGUSR1);
sigprocmask (SIG_SETMASK, &mask, NULL); 
sigwaitinfo(&mask, &info);
sigprocmask (SIG_UNBLOCK, &mask, NULL);

儿童代码:

kill(getppid(), SIGUSR1);

2 个答案:

答案 0 :(得分:1)

检查以下示例程序 example.c

#define _POSIX_C_SOURCE  200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

static inline const char *signal_name(const int signum)
{
    switch (signum) {
    case SIGINT:  return "SIGINT";
    case SIGHUP:  return "SIGHUP";
    case SIGTERM: return "SIGTERM";
    case SIGQUIT: return "SIGQUIT";
    case SIGUSR1: return "SIGUSR1";
    case SIGUSR2: return "SIGUSR2";
    default:      return "(unnamed)";
    }    
}

int main(void)
{
    sigset_t  mask;
    siginfo_t info;
    pid_t     child, p;
    int       signum;    

    sigemptyset(&mask);
    sigaddset(&mask, SIGINT);
    sigaddset(&mask, SIGHUP);
    sigaddset(&mask, SIGTERM);
    sigaddset(&mask, SIGQUIT);
    sigaddset(&mask, SIGUSR1);
    sigaddset(&mask, SIGUSR2);
    if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) {
        fprintf(stderr, "Cannot block SIGUSR1: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }

    child = fork();
    if (child == -1) {
        fprintf(stderr, "Cannot fork a child process: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    } else
    if (!child) {
        /* This is the child process. */
        printf("Child process %d sleeping for 3 seconds ...\n", (int)getpid());
        fflush(stdout);
        sleep(3);

        printf("Child process %d sending SIGUSR1 to parent process (%d) ...\n", (int)getpid(), (int)getppid());
        fflush(stdout);
        kill(getppid(), SIGUSR1);

        printf("Child process %d exiting.\n", (int)getpid());
        return EXIT_SUCCESS;
    }

    /* This is the parent process. */
    printf("Parent process %d is waiting for signals.\n", (int)getpid());
    fflush(stdout);

    while (1) {

        signum = sigwaitinfo(&mask, &info);
        if (signum == -1) {

            /* If some other signal was delivered to a handler installed
               without SA_RESTART in sigaction flags, it will interrupt
               slow calls like sigwaitinfo() with EINTR error. So, those
               are not really errors. */
            if (errno == EINTR)
                continue;

            printf("Parent process: sigwaitinfo() failed: %s.\n", strerror(errno));
            return EXIT_FAILURE;
        }

        if (info.si_pid == child)
            printf("Parent process: Received signal %d (%s) from child process %d.\n", signum, signal_name(signum), (int)child);
        else
        if (info.si_pid)
            printf("Parent process: Received signal %d (%s) from process %d.\n", signum, signal_name(signum), (int)info.si_pid);
        else
            printf("Parent process: Received signal %d (%s).\n", signum, signal_name(signum));
        fflush(stdout);

        /* Exit when SIGUSR1 received from child process. */
        if (signum == SIGUSR1 && info.si_pid == child) {
            printf("Parent process: Received SIGUSR1 from child.\n");
            break;
        }

        /* Also exit if Ctrl+C pressed in terminal (SIGINT). */
        if (signum == SIGINT && !info.si_pid) {
            printf("Parent process: Ctrl+C pressed.\n");
            break;
        }
    }

    printf("Reaping child process...\n");
    fflush(stdout);

    do {
        p = waitpid(child, NULL, 0);
        if (p == -1) {
            if (errno == EINTR)
                continue;
            printf("Parent process: waitpid() failed: %s.\n", strerror(errno));
            return EXIT_FAILURE;
        }
    } while (p != child);

    printf("Done.\n");
    return EXIT_SUCCESS;
}

使用例如

编译并运行
gcc -Wall -O2 example.c -o example
./example

如果要从外部向父进程注入信号,则可以增加子进程的睡眠时间。如果父进程ID为24316,则可以将其发送。通过kill -HUP 24316从另一个端子发出SIGHUP信号。如果按上述方式交互运行示例,则还可以通过按 Ctrl + C 使终端向过程发送SIGINT信号。

观察:

  • sigprocmask()用于在fork()之前阻塞父进程中的有趣信号,以确保父进程能够捕获信号。这也意味着信号在子进程中被阻止。

    如果信号在fork()之后的 中被阻塞,那么子进程可能会在父进程准备好捕获它之前发送信号。对于SIGUSR1,default action将终止父进程。

  • signal_name()功能仅用于精美显示信号名称。

    主要将其标记为static inline,以便我们人类开发人员理解它是仅在当前编译单元中可见的辅助函数。对于编译器,static表示该功能仅在当前编译单元中可见,inline表示该编译器可以自由地将其功能合并到调用它的任何人中,而无需调用命名函数。 / p>

    返回值为const char *,因为该函数返回字符串文字。

  • 如果出现错误,
  • fork()可以返回-1。

  • fork()返回两次。在父进程中,返回值为正;子进程ID。在子进程中,返回值为零。

    新的子进程本质上是父进程的快照副本。它们开始执行的顺序基本上是随机的:它们可以同时同时运行;它们可以同时运行。或者一个可以先运行,另一个可以稍晚一些。如今的计算机是如此之快,以至于以微秒为单位的“足够快”之类的概念仍然可能会导致错误,因此我们需要小心并理解大局。因此,请尽早设置信号屏蔽。

  • 如果发生错误,许多函数将返回-1或NULL,并且将errno设置为指示错误。编写代码时,应始终执行错误检查。它们使您可以在测试代码时检测逻辑和功能错误。在极少数情况下,它们会“降低”任何速度,您始终可以在分析和测试后将其删除。在实践中,每次都值得这样做。如果不是为了其他目的,则是为了捕捉错误的程序员期望。

  • 请参阅man 2 sigactionman 7 signal,以查看哪些信号填充了哪个siginfo_t字段,以及如何确定信号是否由另一个进程发送(通过{{1 }}或kill()),由POSIX计时器触发等。

  • 请参阅waitpid()的while循环以了解如何获得子进程。我们可以使用第二个参数,即指向int的指针以及sigqueue() / WIFEXITED()WEXITSTATUS() / WIFSIGNALED()来检查子进程的退出状态。我没有打扰,因为子进程始终返回WTERMSIG(),在POSIXy系统中该值为0。

  • 学习从模块化的片段设计和构建程序,而不是将所有内容汇总到一起,然后尝试对其进行整理。

    如果将子流程操作拆分为单独的函数,则可以使上面的示例更容易理解。

    但是,切入函数并不是目的:它只是一种工具,用于使代码尽可能简单,易于理解和维护。人类的脑力有限,但是如果我们正确地聚焦,我们可以创造出令人惊奇的东西。

  • 好的注释至少和好的代码一样重要。

    描述代码在做什么的注释一点都不值钱。我们可以阅读代码,看看它能做什么。代码没有告诉我们的是为什么程序员编写了代码:代码的目的是什么,代码试图实现的逻辑模型或算法是什么。

    在示例程序中只有五个注释。这还不够;但是即使经过几十年的专业代码编写,我仍在努力编写更好的注释。 (通常,使用线性文本很难描述我的思维结构。这就像是通过阅读学习了一种语言,却无法发音或理解口头语言一样。)如果我学会了写好评论当我学会编写好的代码时,我将省去很多精力。

    我建议您避免沮丧,并花精力学习如何从一开始就撰写好的评论。


这是另一个示例 example2.c ,它在父进程和子进程之间发出了更多的信号:

EXIT_SUCCESS

您可以试验信号和孩子退出状态,因为这也可以报告。

要开发一个信号乒乓方案,我建议先将其写为时间轴。例如:

#define _POSIX_C_SOURCE  200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

static inline const char *signal_name(const int signum)
{
    switch (signum) {
    case SIGINT:  return "SIGINT";
    case SIGHUP:  return "SIGHUP";
    case SIGTERM: return "SIGTERM";
    case SIGQUIT: return "SIGQUIT";
    case SIGUSR1: return "SIGUSR1";
    case SIGUSR2: return "SIGUSR2";
    default:      return "(unnamed)";
    }    
}

int child_process(const pid_t parent, sigset_t *mask)
{
    siginfo_t    info;
    int          signum;

    printf("Child: sleep(1).\n");
    fflush(stdout);
    sleep(1);

    printf("Child: Sending SIGUSR1 to parent.\n");
    fflush(stdout);
    kill(parent, SIGUSR1);

    printf("Child: Waiting for a SIGUSR2 from parent.\n");
    fflush(stdout);
    while (1) {
        signum = sigwaitinfo(mask, &info);
        if (signum == SIGUSR2 && info.si_pid == parent) {
            printf("Child: Received SIGUSR2 from parent.\n");
            break;
        }

        if (info.si_pid == parent)
            printf("Child: Received %s from parent.\n", signal_name(signum));
        else
        if (info.si_pid)
            printf("Child: Received %s from process %d.\n", signal_name(signum), (int)info.si_pid);
        else
            printf("Child: Received %s.\n", signal_name(signum));
        fflush(stdout);
    }

    printf("Child: Sending SIGHUP to parent.\n");
    fflush(stdout);
    kill(parent, SIGHUP);

    printf("Child: sleep(1).\n");
    fflush(stdout);
    sleep(1);

    printf("Child: Done.\n");
    return EXIT_SUCCESS;
}

void parent_process(const pid_t child, sigset_t *mask)
{
    siginfo_t  info;
    int        signum;

    printf("Parent: Waiting for a SIGUSR1 from child.\n");
    while (1) {
        signum = sigwaitinfo(mask, &info);
        if (signum == SIGUSR1 && info.si_pid == child) {
            printf("Parent: Received SIGUSR1 from child.\n");
            break;
        }

        if (info.si_pid == child)
            printf("Parent: Received %s from child.\n", signal_name(signum));
        else
        if (info.si_pid)
            printf("Parent: Received %s from process %d.\n", signal_name(signum), (int)info.si_pid);
        else
            printf("Parent: Received %s.\n", signal_name(signum));
        fflush(stdout);
    }

    printf("Parent: sleep(1).\n");
    fflush(stdout);
    sleep(1);

    printf("Parent: Sending SIGUSR2 to child.\n");
    fflush(stdout);
    kill(child, SIGUSR2);

    printf("Parent: Waiting for a SIGHUP from child.\n");
    while (1) {
        signum = sigwaitinfo(mask, &info);
        if (signum == SIGHUP && info.si_pid == child) {
            printf("Parent: Received SIGHUP from child.\n");
            break;
        }

        if (info.si_pid == child)
            printf("Parent: Received %s from child.\n", signal_name(signum));
        else
        if (info.si_pid)
            printf("Parent: Received %s from process %d.\n", signal_name(signum), (int)info.si_pid);
        else
            printf("Parent: Received %s.\n", signal_name(signum));
        fflush(stdout);
    }

    return;
}

int main(void)
{
    sigset_t  mask;
    pid_t     child, p;
    int       status;

    sigemptyset(&mask);
    sigaddset(&mask, SIGINT);
    sigaddset(&mask, SIGHUP);
    sigaddset(&mask, SIGTERM);
    sigaddset(&mask, SIGQUIT);
    sigaddset(&mask, SIGUSR1);
    sigaddset(&mask, SIGUSR2);
    if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) {
        fprintf(stderr, "Cannot block SIGUSR1: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    }

    child = fork();
    if (child == -1) {
        fprintf(stderr, "Cannot fork a child process: %s.\n", strerror(errno));
        return EXIT_FAILURE;
    } else
    if (!child)
        return child_process(getppid(), &mask);
    else
        parent_process(child, &mask);

    printf("Parent: Reaping child process.\n");
    fflush(stdout);

    do {
        p = waitpid(child, &status, 0);
        if (p == -1) {
            if (errno == EINTR)
                continue;
            printf("Parent: waitpid() error: %s.\n", strerror(errno));
            return EXIT_FAILURE;
        }
    } while (p != child);

    if (WIFEXITED(status)) {
        switch (WEXITSTATUS(status)) {
        case EXIT_SUCCESS:
            printf("Parent: Child reaped; EXIT_SUCCESS.\n");
            break;
        case EXIT_FAILURE:
            printf("Parent: Child reaped; EXIT_FAILURE.\n");
            break;
        default:
            printf("Parent: Child reaped; exit status %d.\n", WEXITSTATUS(status));
        }
    } else
    if (WIFSIGNALED(status)) {
        printf("Parent: Child died from signal %d.\n", WTERMSIG(status));
    } else {
        printf("Parent: Child process was lost unexpectedly.\n");
    }

    return EXIT_SUCCESS;
}

这样的图有助于编写代码,检查输出是否符合期望(请注意并非所有事件都井井有条),并检查代码是否正确实现了图。它也应该是程序文档的一部分。

答案 1 :(得分:0)

来自this link

  

sigwaitinfo()暂停执行调用线程,直到出现以下情况之一   集合中的信号未决(如果集合中的信号之一是
  已经sigwaitinfo()已经挂起的调用线程,将返回
  立即。)

     

sigwaitinfo()从待处理信号集中删除信号   并返回信号编号作为其功能结果。如果信息   参数不是NULL,则它指向的缓冲区用于
  返回包含
type siginfo_t结构   有关信号的信息。

     

如果设置中的多个信号正等待呼叫者处理,则该信号
  s igwaitinfo()检索到的值是根据
  通常的订购规则。

我想知道与sigwait的区别,就在这里

  

sigwait()函数挂起调用线程的执行   直到信号集中指定的信号之一变为   待定。该功能接受信号(将其从   待处理的信号列表),并以信号形式返回信号编号。

     

sigwait()的操作与sigwaitinfo相同,除了
  

     
      
  • sigwait()仅返回信号号,而不返回siginfo_t    描述信号的结构。

  •   
  • 两个函数的返回值不同。

  •   

函数的语法为:

int sigwaitinfo(const sigset_t *set, siginfo_t *info);