【发布时间】: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 是您的操作系统)另外,发布您的信号处理代码。 -
可能是因为您的信号处理程序(假设您有一个)没有关闭管道(当前正在使用)?也许值得一试。
-
好的,我会在周一恢复工作时添加更多代码。谢谢。