【问题标题】:SIGINT signal gets dropped during write to a pipeSIGINT 信号在写入管道期间被丢弃
【发布时间】:2023-03-03 09:30:22
【问题描述】:

我有一个程序,它使用 pcap_dump 函数将使用 libpcap 收集的 pcap 数据转储到 stdout,其中 stdout 作为 FILE *。 SIGINT 需要进行一些清理,所以我使用 sigaction() 来处理。这在从 shell 执行时效果很好。

但是,该程序旨在由另一个程序调用,这似乎不起作用。这个“调用者”程序调用了一个 pipe(),然后是一个 fork(),然后子的 stdout 文件描述符被关闭,并替换为管道的写端。最后在子进程中执行前面提到的pcap程序。这样 pcap 数据通过管道写入调用程序。这也很好用。但是,当我在写入管道时向子进程发送 SIGINT 时(好吧,pcap 程序认为它写入到 stdout,但它的文件描述符已更改),信号似乎被丢弃了,并且信号处理函数永远不会被调用。

这是为什么呢?如果我将 pcap 数据写入 stderr 或文件,则 SIGINT 永远不会被丢弃。仅在写入管道时。

这是我们设置管道/分叉/执行的方式:

int fd[2];

//Create pipe
pipe(fd);

pid = fork(); //We forked a child

if(pid == 0){ //We are the child now

    close(1); //close child's stdout

    dup(fd[1]); //duplicate child's stdout to the write end of the pipe

    close( fd[0]); //close unused file descriptors
    close( fd[1]);

    //Load the new program
    execlp("./collectraw", "collectraw", NULL);

    perror("Exec");
    exit(127); //Should never get called but we leave it so the child
    //doesnt accidently keep executing
}
else{ //We are the parent

    //Set up the file descriptors
    close(fd[1]);
}

然后杀死我们使用的孩子:

kill( pid, SIGINT);

在 child 中,我们 pcap_loop() 的回调函数可以很简单:

void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet){
    write(1,"<pretend this is like a thousand zeros>",1000); //write to stdout, which is really a pipe
}

而且我们基本上总是会放弃 SIGINT。顺便说一句,有很多数据包要捕获,所以假设它几乎总是在回调函数中是相当安全的。

但是如果我们改变

write(1,... ); //write to stdout, which is really a pipe

write(2,...); //write to stderr, or writing to a file would work too

然后一切都会再次变得笨拙。

为什么我们的 SIGINT 在写入管道时会被丢弃?

感谢您的帮助。

编辑:根本没有调用孩子的 SIGINT 处理程序,但原因并不是孩子的问题,而是父母的问题。我曾经像这样杀死孩子:

if( kill( pid, SIGINT) == -1){
    perror("Could not kill child");
}
close(pipefd);
fprintf(stdout, "Successfully killed child\n");

这曾经是我们的 SIGCHLD 处理程序:

void handlesigchild(int sig) {
    wait();

    printf("Cleaned up a child\n");
}

因此,如已接受的答案中所述,立即关闭管道会导致我们的孩子在处理 SIGINT 之前使用 SIGPIPE 退出。我们刚刚将 close(pipefd) 移至 SIGCHLD 处理程序,它现在可以工作了。

【问题讨论】:

  • 欢迎来到 Stack Overflow。请尽快阅读About 页面。一般来说,您应该选择 C ​​或 C++ 作为语言标签,而不是两者都选——因为适合 C++ 的解决方案通常不适合 C,反之亦然。
  • 运行strace下的进程会显示什么? (假设 Linux 是您的操作系统)另外,发布您的信号处理代码。
  • 可能是因为您的信号处理程序(假设您有一个)没有关闭管道(当前正在使用)?也许值得一试。
  • 好的,我会在周一恢复工作时添加更多代码。谢谢。

标签: c++ pipe signals


【解决方案1】:

您没有显示足够多的代码来了解发生了什么。如果您希望人们能够对您的程序发表评论,您应该始终尝试构建一个 SSCCE 并发布它。

最佳猜测:您的父级在发送信号后退出,关闭管道的读取端。这会导致客户端在有机会处理 SIGINT 之前立即使用 SIGPIPE 退出。尝试对 SIGPIPE 进行清理,或者忽略 SIGPIPE。

【讨论】:

  • 太棒了,谢谢。我们的父级实际上并没有退出,但是它在将 SIGINT 发送给子级后立即关闭了管道,这具有相同的效果。我将更新原始帖子以显示发生了什么。
猜你喜欢
  • 1970-01-01
  • 2017-09-29
  • 2018-09-03
  • 1970-01-01
  • 2021-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-03
相关资源
最近更新 更多