我正在尝试让这个过程等待他儿子的信号。它似乎不起作用。我对 C 不太感兴趣,所以我的代码可能很糟糕。这是代码:
家长代码:
sigemptyset (&mask);
sigaddset (&mask, SIGUSR1);
sigprocmask (SIG_SETMASK, &mask, NULL);
sigwaitinfo(&mask, &info);
sigprocmask (SIG_UNBLOCK, &mask, NULL);
Run Code Online (Sandbox Code Playgroud)
儿童代码:
kill(getppid(), SIGUSR1);
Run Code Online (Sandbox Code Playgroud)
检查以下示例程序example.c:
\n\n#define _POSIX_C_SOURCE 200809L\n#include <stdlib.h>\n#include <unistd.h>\n#include <sys/types.h>\n#include <sys/wait.h>\n#include <signal.h>\n#include <string.h>\n#include <stdio.h>\n#include <errno.h>\n\nstatic inline const char *signal_name(const int signum)\n{\n switch (signum) {\n case SIGINT: return "SIGINT";\n case SIGHUP: return "SIGHUP";\n case SIGTERM: return "SIGTERM";\n case SIGQUIT: return "SIGQUIT";\n case SIGUSR1: return "SIGUSR1";\n case SIGUSR2: return "SIGUSR2";\n default: return "(unnamed)";\n } \n}\n\nint main(void)\n{\n sigset_t mask;\n siginfo_t info;\n pid_t child, p;\n int signum; \n\n sigemptyset(&mask);\n sigaddset(&mask, SIGINT);\n sigaddset(&mask, SIGHUP);\n sigaddset(&mask, SIGTERM);\n sigaddset(&mask, SIGQUIT);\n sigaddset(&mask, SIGUSR1);\n sigaddset(&mask, SIGUSR2);\n if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) {\n fprintf(stderr, "Cannot block SIGUSR1: %s.\\n", strerror(errno));\n return EXIT_FAILURE;\n }\n\n child = fork();\n if (child == -1) {\n fprintf(stderr, "Cannot fork a child process: %s.\\n", strerror(errno));\n return EXIT_FAILURE;\n } else\n if (!child) {\n /* This is the child process. */\n printf("Child process %d sleeping for 3 seconds ...\\n", (int)getpid());\n fflush(stdout);\n sleep(3);\n\n printf("Child process %d sending SIGUSR1 to parent process (%d) ...\\n", (int)getpid(), (int)getppid());\n fflush(stdout);\n kill(getppid(), SIGUSR1);\n\n printf("Child process %d exiting.\\n", (int)getpid());\n return EXIT_SUCCESS;\n }\n\n /* This is the parent process. */\n printf("Parent process %d is waiting for signals.\\n", (int)getpid());\n fflush(stdout);\n\n while (1) {\n\n signum = sigwaitinfo(&mask, &info);\n if (signum == -1) {\n\n /* If some other signal was delivered to a handler installed\n without SA_RESTART in sigaction flags, it will interrupt\n slow calls like sigwaitinfo() with EINTR error. So, those\n are not really errors. */\n if (errno == EINTR)\n continue;\n\n printf("Parent process: sigwaitinfo() failed: %s.\\n", strerror(errno));\n return EXIT_FAILURE;\n }\n\n if (info.si_pid == child)\n printf("Parent process: Received signal %d (%s) from child process %d.\\n", signum, signal_name(signum), (int)child);\n else\n if (info.si_pid)\n printf("Parent process: Received signal %d (%s) from process %d.\\n", signum, signal_name(signum), (int)info.si_pid);\n else\n printf("Parent process: Received signal %d (%s).\\n", signum, signal_name(signum));\n fflush(stdout);\n\n /* Exit when SIGUSR1 received from child process. */\n if (signum == SIGUSR1 && info.si_pid == child) {\n printf("Parent process: Received SIGUSR1 from child.\\n");\n break;\n }\n\n /* Also exit if Ctrl+C pressed in terminal (SIGINT). */\n if (signum == SIGINT && !info.si_pid) {\n printf("Parent process: Ctrl+C pressed.\\n");\n break;\n }\n }\n\n printf("Reaping child process...\\n");\n fflush(stdout);\n\n do {\n p = waitpid(child, NULL, 0);\n if (p == -1) {\n if (errno == EINTR)\n continue;\n printf("Parent process: waitpid() failed: %s.\\n", strerror(errno));\n return EXIT_FAILURE;\n }\n } while (p != child);\n\n printf("Done.\\n");\n return EXIT_SUCCESS;\n}\nRun Code Online (Sandbox Code Playgroud)\n\n使用例如编译并运行它
\n\ngcc -Wall -O2 example.c -o example\n./example\nRun Code Online (Sandbox Code Playgroud)\n\n如果您想从外部向父进程注入信号,您可以增加子进程的睡眠时间。如果父进程 ID 是 24316,您可以发送它,例如:来自另一个终端的 SIGHUP 信号kill -HUP 24316。Ctrl如果如上所示以交互方式运行该示例,您还可以通过按+使终端向进程发送 SIGINT 信号C。
观察结果:
\n\nsigprocmask()用于之前阻塞父进程中感兴趣的信号fork(),以确保父进程能够捕获该信号。这也意味着信号在子进程中被阻止。
如果信号在 后在父进程中被阻塞,则fork()子进程有可能在父进程准备好捕获信号之前发送信号。对于 SIGUSR1,默认操作将终止父进程。
signal_name()函数的存在只是为了漂亮地打印信号名称。
它的标记static inline主要是为了让我们人类开发人员理解它是一个仅在当前编译单元中可见的辅助函数。对于编译器来说,static该函数仅在当前编译单元中可见,并且inline编译器可以自由地将其功能合并到调用它的任何人中,而不是调用命名函数。
返回值为const char *,因为该函数返回一个字符串文字。
fork()如果发生错误可以返回-1。
fork()返回两次。在父进程中,返回值为正;子进程 ID。在子进程中,返回值为零。
新的子进程本质上是父进程的快照副本。它们开始执行的顺序基本上是随机的:它们可以同时运行;或者一个可以先跑,另一个稍后跑。如今计算机的速度如此之快,以微秒为单位的“很快”之类的概念仍然可能会导致错误,因此我们需要小心并了解更大的情况。因此,尽早设置信号掩码。
如果发生错误,许多函数会返回 -1 或 NULL,并用errnoset 来指示错误。编写代码时,应该始终执行错误检查。它们允许您在测试代码时检测逻辑和功能错误。在极少数情况下,它们会“减慢”任何速度,您始终可以在分析和测试后删除它们。实际上,每一次都是值得的。如果不是为了别的什么,就是为了捕捉不正确的程序员期望。
查看man 2 sigaction并man 7 signal查看哪些信号填充哪些siginfo_t字段,以及如何确定信号是否由另一个进程(通过kill()或sigqueue())发送、引发、由 POSIX 计时器触发等。
请参阅 while 循环,waitpid()了解如何获取子进程。我们可以使用第二个参数(指向 int 的指针)和WIFEXITED()/WEXITSTATUS()和WIFSIGNALED()/WTERMSIG()来检查子进程的退出状态。我没有打扰,因为子进程总是返回EXIT_SUCCESS,在 POSIXy 系统中为 0。
学习从模块化的部分设计和构建程序,而不是将所有内容聚集成一堆,然后尝试将其整理出来。
\n\n如果我们将子进程操作拆分为单独的函数,则可以使上面的示例更容易理解。
\n\n然而,拆分为函数并不是一个目标:它只是一个工具,用于使代码尽可能简单、易于理解和维护。我们人类的脑力有限,但如果我们正确地集中注意力,我们可以创造出令人惊奇的东西。
好的注释至少和好的代码一样重要。
\n\n描述代码正在做什么的注释并非毫无价值。我们可以阅读代码,看看它做了什么。代码没有告诉我们的是程序员为什么要编写代码:代码的目的是什么,代码试图实现的逻辑模型或算法是什么。
\n\n示例程序中只有五个注释。这还不够;但即使在专业编写代码几十年之后,我仍在努力编写更好的注释。(大多数情况下,使用线性文本很难描述我思考的心理结构。这就像通过阅读学习一门语言,但无法发音或理解口语。)如果我学会写好的评论当我学会编写好的代码时,我会节省很多精力。
\n\n我建议您避免沮丧,并花精力学习如何从一开始就写出好的评论。
这是另一个示例example2.c,它在父进程和子进程之间进行了更多的信号乒乓操作:
\n\n#define _POSIX_C_SOURCE 200809L\n#include <stdlib.h>\n#include <unistd.h>\n#include <sys/types.h>\n#include <sys/wait.h>\n#include <signal.h>\n#include <string.h>\n#include <stdio.h>\n#include <errno.h>\n\nstatic inline const char *signal_name(const int signum)\n{\n switch (signum) {\n case SIGINT: return "SIGINT";\n case SIGHUP: return "SIGHUP";\n case SIGTERM: return "SIGTERM";\n case SIGQUIT: return "SIGQUIT";\n case SIGUSR1: return "SIGUSR1";\n case SIGUSR2: return "SIGUSR2";\n default: return "(unnamed)";\n } \n}\n\nint child_process(const pid_t parent, sigset_t *mask)\n{\n siginfo_t info;\n int signum;\n\n printf("Child: sleep(1).\\n");\n fflush(stdout);\n sleep(1);\n\n printf("Child: Sending SIGUSR1 to parent.\\n");\n fflush(stdout);\n kill(parent, SIGUSR1);\n\n printf("Child: Waiting for a SIGUSR2 from parent.\\n");\n fflush(stdout);\n while (1) {\n signum = sigwaitinfo(mask, &info);\n if (signum == SIGUSR2 && info.si_pid == parent) {\n printf("Child: Received SIGUSR2 from parent.\\n");\n break;\n }\n\n if (info.si_pid == parent)\n printf("Child: Received %s from parent.\\n", signal_name(signum));\n else\n if (info.si_pid)\n printf("Child: Received %s from process %d.\\n", signal_name(signum), (int)info.si_pid);\n else\n printf("Child: Received %s.\\n", signal_name(signum));\n fflush(stdout);\n }\n\n printf("Child: Sending SIGHUP to parent.\\n");\n fflush(stdout);\n kill(parent, SIGHUP);\n\n printf("Child: sleep(1).\\n");\n fflush(stdout);\n sleep(1);\n\n printf("Child: Done.\\n");\n return EXIT_SUCCESS;\n}\n\nvoid parent_process(const pid_t child, sigset_t *mask)\n{\n siginfo_t info;\n int signum;\n\n printf("Parent: Waiting for a SIGUSR1 from child.\\n");\n while (1) {\n signum = sigwaitinfo(mask, &info);\n if (signum == SIGUSR1 && info.si_pid == child) {\n printf("Parent: Received SIGUSR1 from child.\\n");\n break;\n }\n\n if (info.si_pid == child)\n printf("Parent: Received %s from child.\\n", signal_name(signum));\n else\n if (info.si_pid)\n printf("Parent: Received %s from process %d.\\n", signal_name(signum), (int)info.si_pid);\n else\n printf("Parent: Received %s.\\n", signal_name(signum));\n fflush(stdout);\n }\n\n printf("Parent: sleep(1).\\n");\n fflush(stdout);\n sleep(1);\n\n printf("Parent: Sending SIGUSR2 to child.\\n");\n fflush(stdout);\n kill(child, SIGUSR2);\n\n printf("Parent: Waiting for a SIGHUP from child.\\n");\n while (1) {\n signum = sigwaitinfo(mask, &info);\n if (signum == SIGHUP && info.si_pid == child) {\n printf("Parent: Received SIGHUP from child.\\n");\n break;\n }\n\n if (info.si_pid == child)\n printf("Parent: Received %s from child.\\n", signal_name(signum));\n else\n if (info.si_pid)\n printf("Parent: Received %s from process %d.\\n", signal_name(signum), (int)info.si_pid);\n else\n printf("Parent: Received %s.\\n", signal_name(signum));\n fflush(stdout);\n }\n\n return;\n}\n\nint main(void)\n{\n sigset_t mask;\n pid_t child, p;\n int status;\n\n sigemptyset(&mask);\n sigaddset(&mask, SIGINT);\n sigaddset(&mask, SIGHUP);\n sigaddset(&mask, SIGTERM);\n sigaddset(&mask, SIGQUIT);\n sigaddset(&mask, SIGUSR1);\n sigaddset(&mask, SIGUSR2);\n if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) {\n fprintf(stderr, "Cannot block SIGUSR1: %s.\\n", strerror(errno));\n return EXIT_FAILURE;\n }\n\n child = fork();\n if (child == -1) {\n fprintf(stderr, "Cannot fork a child process: %s.\\n", strerror(errno));\n return EXIT_FAILURE;\n } else\n if (!child)\n return child_process(getppid(), &mask);\n else\n parent_process(child, &mask);\n\n printf("Parent: Reaping child process.\\n");\n fflush(stdout);\n\n do {\n p = waitpid(child, &status, 0);\n if (p == -1) {\n if (errno == EINTR)\n continue;\n printf("Parent: waitpid() error: %s.\\n", strerror(errno));\n return EXIT_FAILURE;\n }\n } while (p != child);\n\n if (WIFEXITED(status)) {\n switch (WEXITSTATUS(status)) {\n case EXIT_SUCCESS:\n printf("Parent: Child reaped; EXIT_SUCCESS.\\n");\n break;\n case EXIT_FAILURE:\n printf("Parent: Child reaped; EXIT_FAILURE.\\n");\n break;\n default:\n printf("Parent: Child reaped; exit status %d.\\n", WEXITSTATUS(status));\n }\n } else\n if (WIFSIGNALED(status)) {\n printf("Parent: Child died from signal %d.\\n", WTERMSIG(status));\n } else {\n printf("Parent: Child process was lost unexpectedly.\\n");\n }\n\n return EXIT_SUCCESS;\n}\nRun Code Online (Sandbox Code Playgroud)\n\n您可以尝试使用信号和子进程退出状态,正如本文所报告的那样。
\n\n要开发信号乒乓方案,我建议首先将其写成时间线。例如:
\n\n#define _POSIX_C_SOURCE 200809L\n#include <stdlib.h>\n#include <unistd.h>\n#include <sys/types.h>\n#include <sys/wait.h>\n#include <signal.h>\n#include <string.h>\n#include <stdio.h>\n#include <errno.h>\n\nstatic inline const char *signal_name(const int signum)\n{\n switch (signum) {\n case SIGINT: return "SIGINT";\n case SIGHUP: return "SIGHUP";\n case SIGTERM: return "SIGTERM";\n case SIGQUIT: return "SIGQUIT";\n case SIGUSR1: return "SIGUSR1";\n case SIGUSR2: return "SIGUSR2";\n default: return "(unnamed)";\n } \n}\n\nint main(void)\n{\n sigset_t mask;\n siginfo_t info;\n pid_t child, p;\n int signum; \n\n sigemptyset(&mask);\n sigaddset(&mask, SIGINT);\n sigaddset(&mask, SIGHUP);\n sigaddset(&mask, SIGTERM);\n sigaddset(&mask, SIGQUIT);\n sigaddset(&mask, SIGUSR1);\n sigaddset(&mask, SIGUSR2);\n if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) {\n fprintf(stderr, "Cannot block SIGUSR1: %s.\\n", strerror(errno));\n return EXIT_FAILURE;\n }\n\n child = fork();\n if (child == -1) {\n fprintf(stderr, "Cannot fork a child process: %s.\\n", strerror(errno));\n return EXIT_FAILURE;\n } else\n if (!child) {\n /* This is the child process. */\n printf("Child process %d sleeping for 3 seconds ...\\n", (int)getpid());\n fflush(stdout);\n sleep(3);\n\n printf("Child process %d sending SIGUSR1 to parent process (%d) ...\\n", (int)getpid(), (int)getppid());\n fflush(stdout);\n kill(getppid(), SIGUSR1);\n\n printf("Child process %d exiting.\\n", (int)getpid());\n return EXIT_SUCCESS;\n }\n\n /* This is the parent process. */\n printf("Parent process %d is waiting for signals.\\n", (int)getpid());\n fflush(stdout);\n\n while (1) {\n\n signum = sigwaitinfo(&mask, &info);\n if (signum == -1) {\n\n /* If some other signal was delivered to a handler installed\n without SA_RESTART in sigaction flags, it will interrupt\n slow calls like sigwaitinfo() with EINTR error. So, those\n are not really errors. */\n if (errno == EINTR)\n continue;\n\n printf("Parent process: sigwaitinfo() failed: %s.\\n", strerror(errno));\n return EXIT_FAILURE;\n }\n\n if (info.si_pid == child)\n printf("Parent process: Received signal %d (%s) from child process %d.\\n", signum, signal_name(signum), (int)child);\n else\n if (info.si_pid)\n printf("Parent process: Received signal %d (%s) from process %d.\\n", signum, signal_name(signum), (int)info.si_pid);\n else\n printf("Parent process: Received signal %d (%s).\\n", signum, signal_name(signum));\n fflush(stdout);\n\n /* Exit when SIGUSR1 received from child process. */\n if (signum == SIGUSR1 && info.si_pid == child) {\n printf("Parent process: Received SIGUSR1 from child.\\n");\n break;\n }\n\n /* Also exit if Ctrl+C pressed in terminal (SIGINT). */\n if (signum == SIGINT && !info.si_pid) {\n printf("Parent process: Ctrl+C pressed.\\n");\n break;\n }\n }\n\n printf("Reaping child process...\\n");\n fflush(stdout);\n\n do {\n p = waitpid(child, NULL, 0);\n if (p == -1) {\n if (errno == EINTR)\n continue;\n printf("Parent process: waitpid() failed: %s.\\n", strerror(errno));\n return EXIT_FAILURE;\n }\n } while (p != child);\n\n printf("Done.\\n");\n return EXIT_SUCCESS;\n}\nRun Code Online (Sandbox Code Playgroud)\n\n这样的图有助于编写代码,检查输出是否符合预期(注意并非所有事件都按顺序排列),并检查代码是否正确实现该图。它也应该是程序文档的一部分。
\n