3 回答
TA贡献1876条经验 获得超7个赞
正如我在对上一个问题的相关回答中所回答的那样,您需要实现一个事件循环;顾名思义,它是循环的,因此您应该在父进程中进行编码:
while (1) { // simplistic event loop! int status=0; if (waitpid(pid, &status, WNOHANG) == pid) { // clean up, child process has ended handle_process_end(status); break; }; struct pollpfd pfd[2]; memset (&pfd, 0, sizeof(pfd)); // probably useless but dont harm pfd[0].fd = rpipe[0]; pfd[0].events = POLL_IN; pfd[1].fd = wpipe[1]; pfd[0].event = POLL_OUT; #define DELAY 5000 /* 5 seconds */ if (poll(pfd, 2, DELAY)>0) { if (pfd[0].revents & POLL_IN) { /* read something from rpipe[0]; detect end of file; you probably need to do some buffering, because you may e.g. read some partial line chunk written by the child, and you could only handle full lines. */ }; if (pfd[1].revents & POLL_OUT) { /* write something on wpipe[1] */ }; } fflush(NULL);} /* end while(1) */
您无法预测管道的可读或可写顺序,并且这种情况可能会发生很多次。当然,涉及很多缓冲(在父进程中),我将详细信息留给您。...您对子进程中的缓冲没有影响(某些程序检测到它们的输出是否为isatty)。
上面类似的事件轮询循环为您避免死锁情况,因为子进程的stdout管道已满,子进程被阻塞,而父进程由于管道已满而被阻止写入(写入子stdin管道)父进程:循环时,只要轮询某些数据在输入管道上即可读取(即子进程的stdout),便会读取数据,并且一旦轮询输出管道可写(即未满)就可以写入一些数据。您无法预先预测这些事件“孩子的输出可被父母读取”和“孩子的输入可被父母写入”的顺序。
我建议阅读高级Linux编程,其中有几章解释了这些问题!
顺便说一句,我的简单事件循环有点不对:如果子进程终止并且某些数据保留在其stdout管道中,则不会完成其读取。您可以waitpid
在poll
另外,不要期望单个write
(来自子进程)进入管道会触发read
父进程中的一个。换句话说,没有消息长度的概念。但是,POSIX知道PIPE_MAX
...。请参阅其编写文档。也许您的缓冲区传递到read
和write
应该的PIPE_MAX
大小。
我重复一遍:您需要在事件循环内调用,poll
并且很有可能poll
会被调用多次(因为循环将重复多次!),并且将以不可预测(且不可重现)的顺序报告可读或可写的管道末端!程序的第一次运行可能报告“ rpipe[0]
可读”,read
从中可以得到324个字节,重复事件循环,poll
说“ wpipe[1]
可写”,可以write
向其重复10个字节,然后重复事件循环,poll
则表明“ rpipe[0]
可读”,您read
从中读取110个字节,重复事件循环,poll
再次告诉“ rpipe[0]
可读”,您read
4096个字节,等等,等等...在相同环境中第二次运行同一程序将产生不同的事件,例如:poll
说“ wpipe[1]
可写”,您write
对它写了1000个字节,重复循环,poll
说“ rpipe[0]
可读”等...
注意:您的问题不是子程序(“客户端”)中的缓冲,我们认为您无法更改。因此,重要的不是其中的缓冲数据,而是真正的输入和输出(这是父进程唯一可以观察到的内容;内部子缓冲区与父进程无关),即子程序能够执行的数据真正 读取(2)和写入(2)。并且如果通过pipe(7),则此类数据将在父进程中变为poll(2)-(并且您的父进程可以在或之后的更新字段中read
或write
其中的一部分)。顺便说一句,如果您确实编码了孩子,别忘了打电话给POLL_IN
POLL_OUT
revents
poll
fflush
在里面合适的地方。
TA贡献1839条经验 获得超15个赞
您的代码中似乎有两个问题。默认情况下,“ stdout”是缓冲的,因此服务器应显式刷新它:
printf("I received %s\n", buffer);fflush(stdout);
并且POLLOUT
在尝试读取时主程序不应注册(但您可能想要注册POLLERR
):
pfds[0].fd = rpipe[0];pfds[0].events = POLLIN | POLLERR;
通过这些修改,您可以获得预期的输出:
$ ./main写入数据...读取数据...孩子说:我打招呼了
通常,您还应该检查的返回值poll()
,并在必要时重复调用(例如,在系统调用中断或超时的情况下)。
TA贡献1796条经验 获得超4个赞
从您的问题中我感觉到您拥有服务器代码,因此可以对其进行修改。即使使用“ cat”,如果您按照pfds[0].events = POLLIN | POLLERR
我的建议进行设置,它似乎也可以工作。但是,如果外部程序对其输出进行缓冲(因此不会写入stdout),那么我认为您无法做任何事情。
- 3 回答
- 0 关注
- 579 浏览
添加回答
举报