我有一个程序,使用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);
在子节点中,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处理程序,它现在可以正常工作。
答案 0 :(得分:2)
您没有显示足够的代码来了解正在发生的事情。如果您希望人们能够对您的计划发表评论,您应该始终尝试构建SSCCE并发布该帖子。
最佳猜测:您的父母在发送信号后退出,关闭管道的读取端。这会导致客户端在有机会处理SIGINT之前立即使用SIGPIPE退出。尝试在SIGPIPE上进行清理,或忽略SIGPIPE。