在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");
}
答案 0 :(得分:1)
您设置的setvbuf选项与stdio流有关,而与文件描述符无关,因此无效。
读取/写入系统调用不会被缓冲(除了缓存不同且内核中可能存在的缓存),因此您不必担心禁用缓冲区或任何其他此类内容。他们将直接前往他们需要去的地方。
话虽如此,它们是阻塞的,所以如果内核没有足够的数据来填充你的IO块大小,它们将在操作系统级别阻塞,直到数据存在并可以复制到缓冲区或从缓冲区复制。如果遇到EOF条件或者您启用了异步/非阻塞IO,它们只会提供少于您要求的数据。
您可以使用fcntl接口通过系统调用启用非阻塞IO。这将立即返回,但并不总是支持,具体取决于您使用文件描述符的方式。通过AIO接口支持异步IO(用于文件)。
答案 1 :(得分:1)
您的缓冲问题源于这样一个事实:缓冲是由您正在执行的程序中的C标准库执行的,而不是在内核/文件描述符级别(由@Claris观察到)。你无法做任何事情来影响另一个程序自己的代码中的缓冲(除非你修改该程序)。
这实际上是任何试图自动与程序交互的人遇到的常见问题。
一种解决方案是使用伪tty,这使得程序认为它实际上是在与交互式终端通信,这会改变它的缓冲行为等等。
This article提供了很好的介绍。那里有一个示例程序,详细说明了如何实现您的目标。