我有一个程序,使用 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
to
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 处理程序,现在它可以工作了。