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]可读”,您read4096个字节,等等,等等...在相同环境中第二次运行同一程序将产生不同的事件,例如:poll说“ wpipe[1]可写”,您write对它写了1000个字节,重复循环,poll说“ rpipe[0]可读”等...
注意:您的问题不是子程序(“客户端”)中的缓冲,我们认为您无法更改。因此,重要的不是其中的缓冲数据,而是真正的输入和输出(这是父进程唯一可以观察到的内容;内部子缓冲区与父进程无关),即子程序能够执行的数据真正 读取(2)和写入(2)。并且如果通过pipe(7),则此类数据将在父进程中变为poll(2)-(并且您的父进程可以在或之后的更新字段中read或write其中的一部分)。顺便说一句,如果您确实编码了孩子,别忘了打电话给POLL_INPOLL_OUTreventspollfflush 在里面合适的地方。
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 关注
- 637 浏览
添加回答
举报
