在写入管道

时间:2015-05-29 02:08:08

标签: c++ pipe signals

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

1 个答案:

答案 0 :(得分:2)

您没有显示足够的代码来了解正在发生的事情。如果您希望人们能够对您的计划发表评论,您应该始终尝试构建SSCCE并发布该帖子。

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