运行 GeeksforGeeks 示例信号代码时仅接收 parent printf
Only receiving parent printf when runnnig GeeksforGeeks sample signal code
我试图将注意力集中在信号上,所以我从 运行在 GeeksforGeeks 信号页面上设置代码开始。在他们的页面上,它显示了 parent 和 child 的输出,但是当我在 CLion 中 运行 时,有 9/10 次我只收到 parent 和child 不打印任何内容。 child 每隔一段时间就会打印一次,但我不知道为什么或如何让它始终如一地打印。
https://www.geeksforgeeks.org/signals-c-set-2/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
void sighup();
void sigint();
void sigquit();
void main() {
int pid;
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
}
if (pid == 0) {
signal(SIGHUP, sighup);
signal(SIGINT, sigint);
signal(SIGQUIT, sigquit);
for(;;)
;
}
else {
printf("\nPARENT: sending SIGUP\n\n");
kill(pid, SIGHUP);
sleep(3);
printf("\nPARENT: sending SIGINT\n\n");
kill(pid, SIGINT);
sleep(3);
printf("\nPARENT: sending SIGQUIT\n\n");
kill(pid, SIGQUIT);
sleep(3);
}
void sighup() {
signal(SIGHUP, sighup);
printf("CHILD: I have received a SIGHUP\n");
}
void sigint() {
signal(SIGINT, sigint);
printf("CHILD: I have received a SIGINT\n");
}
void sigquit() {
printf("My parent has killed me");
exit(0);
}
该代码有 choc-full 个错误。您应该首先在编译器上启用警告。在 GCC 和 clang 上,这是通过传递标志 -pedantic
和警告级别来完成的。我建议使用 -Wall -Wextra -Werror
——这会给出相当严格的警告而不会妨碍。
前两个错误,如推荐中所述,是原型声明错误,以及 main
函数签名错误。
第二个错误是你不能在信号处理程序中安全地调用printf
(你会在代码中遇到这种情况,但这是错误的)。事实上,您可以在信号处理程序中安全执行的一组操作受到 严重 限制 — read the documentation carefully.
第三,代码尝试重置处理程序的尝试是拙劣的(无论如何都是不必要的)。 如果你想重置一个处理程序,你需要传递SIG_DFL
作为第二个参数,而不是自定义回调。文档中也对此进行了解释。
最后,不能保证子进程中的代码在父进程中的代码之前执行——特别是,不能保证 信号处理程序 会被设置。在我的测试中,子代码最终 never 被执行,尽管父代码中有 sleep
语句。这可能看起来很奇怪,但它是完全合法的,需要预料到。
将这些部分放在一起得到以下内容,您可能会注意到它比您尝试的 non-working 代码复杂得多。
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
static volatile sig_atomic_t child_signal;
static volatile sig_atomic_t setup_done = 0;
void child_signal_handler(int);
void parent_signal_handler(int);
int main() {
const int parent_pid = getpid();
int pid;
// Set up BEFORE calling `fork`
signal(SIGCHLD, parent_signal_handler);
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
}
if (pid == 0) {
signal(SIGHUP, child_signal_handler);
signal(SIGINT, child_signal_handler);
signal(SIGQUIT, child_signal_handler);
kill(parent_pid, SIGCHLD);
for (;;) {
if (child_signal != 0) {
switch ((int) child_signal) {
case SIGHUP:
fprintf(stderr, "CHILD: I have received a SIGHUP\n");
break;
case SIGINT:
fprintf(stderr, "CHILD: I have received a SIGINT\n");
break;
case SIGQUIT:
fprintf(stderr, "My parent has killed me");
exit(1);
}
child_signal = 0;
}
}
} else {
while (! setup_done) { sleep(1); }
printf("\nPARENT: sending SIGUP\n");
kill(pid, SIGHUP);
sleep(1);
printf("\nPARENT: sending SIGINT\n");
kill(pid, SIGINT);
sleep(1);
printf("\nPARENT: sending SIGQUIT\n");
kill(pid, SIGQUIT);
}
}
void child_signal_handler(int signo) {
child_signal = signo;
}
void parent_signal_handler(int signo) {
if (signo == SIGCHLD) setup_done = 1;
}
我试图将注意力集中在信号上,所以我从 运行在 GeeksforGeeks 信号页面上设置代码开始。在他们的页面上,它显示了 parent 和 child 的输出,但是当我在 CLion 中 运行 时,有 9/10 次我只收到 parent 和child 不打印任何内容。 child 每隔一段时间就会打印一次,但我不知道为什么或如何让它始终如一地打印。
https://www.geeksforgeeks.org/signals-c-set-2/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
void sighup();
void sigint();
void sigquit();
void main() {
int pid;
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
}
if (pid == 0) {
signal(SIGHUP, sighup);
signal(SIGINT, sigint);
signal(SIGQUIT, sigquit);
for(;;)
;
}
else {
printf("\nPARENT: sending SIGUP\n\n");
kill(pid, SIGHUP);
sleep(3);
printf("\nPARENT: sending SIGINT\n\n");
kill(pid, SIGINT);
sleep(3);
printf("\nPARENT: sending SIGQUIT\n\n");
kill(pid, SIGQUIT);
sleep(3);
}
void sighup() {
signal(SIGHUP, sighup);
printf("CHILD: I have received a SIGHUP\n");
}
void sigint() {
signal(SIGINT, sigint);
printf("CHILD: I have received a SIGINT\n");
}
void sigquit() {
printf("My parent has killed me");
exit(0);
}
该代码有 choc-full 个错误。您应该首先在编译器上启用警告。在 GCC 和 clang 上,这是通过传递标志 -pedantic
和警告级别来完成的。我建议使用 -Wall -Wextra -Werror
——这会给出相当严格的警告而不会妨碍。
前两个错误,如推荐中所述,是原型声明错误,以及 main
函数签名错误。
第二个错误是你不能在信号处理程序中安全地调用printf
(你会在代码中遇到这种情况,但这是错误的)。事实上,您可以在信号处理程序中安全执行的一组操作受到 严重 限制 — read the documentation carefully.
第三,代码尝试重置处理程序的尝试是拙劣的(无论如何都是不必要的)。 如果你想重置一个处理程序,你需要传递SIG_DFL
作为第二个参数,而不是自定义回调。文档中也对此进行了解释。
最后,不能保证子进程中的代码在父进程中的代码之前执行——特别是,不能保证 信号处理程序 会被设置。在我的测试中,子代码最终 never 被执行,尽管父代码中有 sleep
语句。这可能看起来很奇怪,但它是完全合法的,需要预料到。
将这些部分放在一起得到以下内容,您可能会注意到它比您尝试的 non-working 代码复杂得多。
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
static volatile sig_atomic_t child_signal;
static volatile sig_atomic_t setup_done = 0;
void child_signal_handler(int);
void parent_signal_handler(int);
int main() {
const int parent_pid = getpid();
int pid;
// Set up BEFORE calling `fork`
signal(SIGCHLD, parent_signal_handler);
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
}
if (pid == 0) {
signal(SIGHUP, child_signal_handler);
signal(SIGINT, child_signal_handler);
signal(SIGQUIT, child_signal_handler);
kill(parent_pid, SIGCHLD);
for (;;) {
if (child_signal != 0) {
switch ((int) child_signal) {
case SIGHUP:
fprintf(stderr, "CHILD: I have received a SIGHUP\n");
break;
case SIGINT:
fprintf(stderr, "CHILD: I have received a SIGINT\n");
break;
case SIGQUIT:
fprintf(stderr, "My parent has killed me");
exit(1);
}
child_signal = 0;
}
}
} else {
while (! setup_done) { sleep(1); }
printf("\nPARENT: sending SIGUP\n");
kill(pid, SIGHUP);
sleep(1);
printf("\nPARENT: sending SIGINT\n");
kill(pid, SIGINT);
sleep(1);
printf("\nPARENT: sending SIGQUIT\n");
kill(pid, SIGQUIT);
}
}
void child_signal_handler(int signo) {
child_signal = signo;
}
void parent_signal_handler(int signo) {
if (signo == SIGCHLD) setup_done = 1;
}