gpt4 book ai didi

c - 父进程只从其子进程收到一次或两次 SIGCHLD,无论它 fork() 多少次

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:03:20 25 4
gpt4 key购买 nike

我几乎做了和here一样的事情,实现一个signalprocmask程序,其中parent将处理其children发送的所有SIGCHLD。(我也测试了链接中的代码,但结果是一样的 - parent只收到一次或两次SIGCHLD它 fork 了多少次)

预期的结果是:(add的数量与del的数量相同)

add job 12987
add job 12988
Wed Dec 19 22:20:59 CST 2018
del from 12987
Wed Dec 19 22:21:00 CST 2018
del from 12988
add job 12989
add job 12990
del from 12989
Wed Dec 19 22:21:01 CST 2018
add job 12991
Wed Dec 19 22:21:02 CST 2018
del from 12990
Wed Dec 19 22:21:03 CST 2018
del from 12991

但结果是:(并非所有的 SIGCHLD 都会被父进程捕获)

add job 12987
add job 12988
Wed Dec 19 22:20:59 CST 2018
del from 12987
now the list is: 12988
Wed Dec 19 22:21:00 CST 2018
del from 12988
now the list is:
add job 12989
add job 12990
Wed Dec 19 22:21:01 CST 2018
add job 12991
Wed Dec 19 22:21:02 CST 2018
add job 12992
Wed Dec 19 22:21:03 CST 2018
add job 12993
Wed Dec 19 22:21:04 CST 2018
add job 12994
Wed Dec 19 22:21:05 CST 2018
add job 13091
Wed Dec 19 22:21:06 CST 2018
add job 13092
Wed Dec 19 22:21:07 CST 2018
Wed Dec 19 22:21:08 CST 2018

这是我的代码:

#include "apue.h"
#include <sys/wait.h>
#include <sys/signal.h>
#include <errno.h>

void printJobs();
void addJob(int);
void delJob();

void handler(int sig)
{
sigset_t mask_all, pre_all;
sigfillset(&mask_all); // fill all bits of the mask
pid_t pid;
while ((pid = waitpid(-1, NULL, 0)) > 0) {
sigprocmask(SIG_BLOCK, &mask_all, &pre_all);
printf("del from %d\n", pid);
delJob(pid);
sigprocmask(SIG_UNBLOCK, &pre_all, NULL);
}
if (errno != ECHILD)
printf("waitpid error\n");
}

int main(int argc, char **argv)
{
pid_t pid;
sigset_t mask_all, mask_one, pre_one;

sigfillset(&mask_all);
sigemptyset(&mask_one);
sigaddset(&mask_one, SIGCHLD);
signal(SIGCHLD, handler);
for (int i = 0; i < 10; ++i) {
sigprocmask(SIG_BLOCK, &mask_one, &pre_one); // block SIGCHLD
if ((pid = fork()) == 0) {
sigprocmask(SIG_SETMASK, &pre_one, NULL);
sleep(1);
execve("/bin/date", argv, NULL);
}
sigprocmask(SIG_BLOCK, &mask_all, NULL); // block all sigals
addJob(pid);
sigprocmask(SIG_SETMASK, &pre_one, NULL); // unblock SIGCHLD
sleep(1);
}
exit(0);
}

typedef struct Node {
int val;
struct Node *next;
} Node, *pNode;

pNode phead = NULL, ptail = NULL;

void printJobs()
{
pNode pt = phead;
while (pt) {
printf("%d", pt->val);
pt = pt->next;
}
printf("\n");
}

void delJob(int pid)
{
if (ptail) {
pNode pt = phead, pre = NULL;
while (pt && pt->val != pid) {
pre = pt;
pt = pt->next;
}
if (!pt) {
printf("No job %d\n", pid);
return;
}
if (pt == phead) { // only have one node or empty
phead = phead->next ? phead->next : NULL;
free(pt);
ptail = phead ? ptail : NULL;
} else { // have more than one nodes
printf("del %d\n", pt->val);
free(pt);
pre->next = NULL;
ptail = pt == ptail ? pre : ptail;
}
printf("now the list is: ");
printJobs();
} else {
printf("No job %d\n", pid);
}
}

void addJob(int pid)
{
printf("add job %d\n", pid);
pNode pt = malloc(sizeof(Node));
pt->val = pid;
pt->next = NULL;
if (!phead) {
phead = ptail = pt;
} else {
ptail->next = pt;
ptail = pt;
}
}

最佳答案

您的父进程不会等待其子进程完成(通过 wait()waitpid()),因此无法确保 SIGCHLD 当它们发出时发出的信号将在它还活着的时候被传递给它。信号处理程序执行等待在这里没有帮助,因为这不会阻止进程在 child 还活着的时候终止。父级和所有子级可能会在大约同一时间终止,因此父级仅报告来自一两个子级的信号并不奇怪,即使我们假设从信号处理程序的行为与您预期的一样。

此外,所有 sleep() 程序确实可能混淆问题而不是澄清问题。当然,它不是管理进程间同步和计时的正确工具。

考虑这个经过大量修剪和修改的导数以进行比较:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>

volatile sig_atomic_t signal_count;

void handler(int sig) {
signal_count += 1;
}

int main(int argc, char *argv[]) {
pid_t pid;

signal_count = 0;
signal(SIGCHLD, handler);
for (int i = 0; i < 10; ++i) {
if ((pid = fork()) == 0) {
sleep(1);
execve("/bin/date", argv, NULL);
}
sleep(1);
}

// wait for the children to terminate
while (wait(NULL) != -1) { /* empty */ }

printf("Process %d handled %d SIGCHLD signals\n", (int) getpid(), (int) signal_count);
exit(0);
}

我对该程序的测试运行产生了这个输出:

Wed Dec 19 09:56:08 CST 2018
Wed Dec 19 09:56:09 CST 2018
Wed Dec 19 09:56:09 CST 2018
Wed Dec 19 09:56:10 CST 2018
Wed Dec 19 09:56:10 CST 2018
Wed Dec 19 09:56:11 CST 2018
Wed Dec 19 09:56:11 CST 2018
Wed Dec 19 09:56:11 CST 2018
Wed Dec 19 09:56:12 CST 2018
Wed Dec 19 09:56:12 CST 2018
Process 2169 handled 10 SIGCHLD signals

特别注意最后一行。它确认所有 10 个预期信号均由原始父进程处理。

附录

正如@zwol 在评论中观察到的,在某种程度上,父级可以依靠 wait()waitpid() 中的阻塞来收集其子级,它不会根本不需要为 SIGCHLD 注册一个处理程序。它可以在每次 wait() 返回非错误代码时执行任何需要的工作。使用信号处理程序来收集子进程适用于相反的情况,即您希望避免阻塞父进程,或者弄清楚在何处尝试以非阻塞方式收集子进程。

尽管如此,尽管您通常不想阻塞以收集子项,但父项可能希望确保在某个时候——也许在它准备终止时——它收集所有剩余的子项。在这种情况下,在同一程序中使用这两种方法来收集 child 可能是有意义的。

附录 2

感谢@NominalAnimal,我也观察到信号处理的通常实现不会同时将相同类型的多个非实时信号排队到同一线程。如果一个信号被传递到一个已经有该类型信号挂起的线程,那么新信号不会产生额外的效果。出于这个原因,尽管我证明每个 child 都收到了一个单独的 SIGCHLD,但我不能保证看到超过一个,因为第二个到第十个可能会在第一个仍未完成时交付。保持信号处理程序的实现较短可以降低以这种方式“丢失”信号的可能性,但不能消除它。

但是请注意,这些特殊的信号处理细节不会阻止 wait()waitpid() 收集进程终止的子进程。

关于c - 父进程只从其子进程收到一次或两次 SIGCHLD,无论它 fork() 多少次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53853755/

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