在写入管道期间,SIGINT信号被丢弃

rex*_*oni 6 c++ signals pipe

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

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

这是为什么?如果我将pcap数据写入stderr或文件,SIGINT永远不会被删除.只有在写入管道时.

以下是我们如何设置管道/ fork/execute:

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]);
}
Run Code Online (Sandbox Code Playgroud)

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

kill( pid, SIGINT);
Run Code Online (Sandbox Code Playgroud)

在子代中,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
}
Run Code Online (Sandbox Code Playgroud)

我们基本上总是放弃SIGINT.顺便说一下,有很多数据包需要捕获,所以假设它几乎总是在回调函数中,这是非常安全的.

但是如果我们改变了

write(1,... ); //write to stdout, which is really a pipe
Run Code Online (Sandbox Code Playgroud)

write(2,...); //write to stderr, or writing to a file would work too
Run Code Online (Sandbox Code Playgroud)

然后一切都变得笨拙了.

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

谢谢你的帮助.

编辑:孩子的SIGINT处理程序根本就没有被调用,但原因并不是孩子真正的问题,这是父母的一个问题.我曾经像孩子一样杀了孩子:

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

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

void handlesigchild(int sig) {
    wait();

    printf("Cleaned up a child\n");
}
Run Code Online (Sandbox Code Playgroud)

因此,如接受的答案中所述,立即关闭管道导致我们的孩子在处理SIGINT之前使用SIGPIPE退出.我们只是将close(pipefd)移动到SIGCHLD处理程序,它现在正常工作.

Chr*_*odd 2

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

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