如何确保子进程最终在C中写入数据?

时间:2014-08-16 00:26:05

标签: c exec pipe

在C中,我想分叉子进程,并将其STDIN和STDOUT映射到管道。然后,父母通过写信或阅读孩子的STDIN和STDOUT与孩子沟通。

下面的MWE代码显然是成功的。父线程接收字符串"发送一些消息",我可以通过写入stdout将任意消息发送到父线程。我也可以使用例如父母自由阅读来自父母的消息。 scanf的。

问题在于,一旦孩子调用execl,输出似乎就会停止。我知道没有调用setvbuf到unbuffer stdout,这段代码将无限期挂起,所以我想调用execl重新缓冲stdout。由于子程序./a.out本身是交互式的,我们遇到了一个竞争条件,孩子不会写(因为缓冲),并且阻塞等待输入,而父块在生成输入之前等待孩子写对于孩子。

有没有一种避免这种情况的好方法?特别是,有没有办法使用不会覆盖stdin stdout等属性的exec?

int main(char* argv[], int argc){
        int mgame_read_pipe[2];
        int mgame_write_pipe[2];
        pipe(mgame_read_pipe);
        pipe(mgame_write_pipe);
        pid_t is_child = fork();
        if(is_child == -1){
                perror("Error while forking.");
                exit(1);
        }
        if(is_child==0){
                dup2(mgame_read_pipe[1], STDOUT_FILENO);
                printf("Sending some message.\n");
                dup2(mgame_write_pipe[0], STDIN_FILENO);
                setvbuf(stdin, NULL, _IONBF, 0);
                setvbuf(stdout, NULL, _IONBF, 0);
                close(mgame_read_pipe[0]);
                close(mgame_write_pipe[1]);
                execl("./a.out", "./a.out", NULL);
        }
        else{
                close(mgame_read_pipe[1]);
                close(mgame_write_pipe[0]);

                int status;
                do{
                        printf("SYSTEM: Waiting for inferior process op.\n");
                        char buf[BUFSIZ];
                        read(mgame_read_pipe[0], buf, BUFSIZ);
                        printf("%s",buf);
                        scanf("%s", buf);
                        printf("SYSTEM: Waiting for inferior process ip.\n");
                        write(mgame_write_pipe[1], buf, strlen(buf));
                } while( !waitpid(is_child, &status, WNOHANG) );
        }
}

编辑:为了完整性,这是一个(未经测试的)示例a.out:

int main(){
    printf("I'm alive!");
    int parent_msg;
    scanf("%d", &parent_msg);
    printf("I got %d\n");
}

2 个答案:

答案 0 :(得分:1)

您设置的setvbuf选项与stdio流有关,而与文件描述符无关,因此无效。

读取/写入系统调用不会被缓冲(除了缓存不同且内核中可能存在的缓存),因此您不必担心禁用缓冲区或任何其他此类内容。他们将直接前往他们需要去的地方。

话虽如此,它们是阻塞的,所以如果内核没有足够的数据来填充你的IO块大小,它们将在操作系统级别阻塞,直到数据存在并可以复制到缓冲区或从缓冲区复制。如果遇到EOF条件或者您启用了异步/非阻塞IO,它们只会提供少于您要求的数据。

您可以使用fcntl接口通过系统调用启用非阻塞IO。这将立即返回,但并不总是支持,具体取决于您使用文件描述符的方式。通过AIO接口支持异步IO(用于文件)。

答案 1 :(得分:1)

您的缓冲问题源于这样一个事实:缓冲是由您正在执行的程序中的C标准库执行的,而不是在内核/文件描述符级别(由@Claris观察到)。你无法做任何事情来影响另一个程序自己的代码中的缓冲(除非你修改该程序)。

这实际上是任何试图自动与程序交互的人遇到的常见问题。

一种解决方案是使用伪tty,这使得程序认为它实际上是在与交互式终端通信,这会改变它的缓冲行为等等。

This article提供了很好的介绍。那里有一个示例程序,详细说明了如何实现您的目标。