使用 Linux pipeline() 的进程编程模型
来自http://pubs.opengroup.org/onlinepubs/009604599/functions/pipe。 html:
pipe()函数应创建一个管道并放置两个文件 描述符,每个进入参数 fildes[0] 和 fildes[1],即 读写端参见打开文件说明 管道。
有一个父级向其子级写入数据的示例:
int fildes[2];
const int BSIZE = 100;
char buf[BSIZE];
ssize_t nbytes;
int status;
status = pipe(fildes);
if (status == -1 ) {
/* an error occurred */
...
}
switch (fork()) {
case -1: /* Handle error */
break;
case 0: /* Child - reads from pipe */
close(fildes[1]); /* Write end is unused */
nbytes = read(fildes[0], buf, BSIZE); /* Get data from pipe */
/* At this point, a further read would see end of file ... */
close(fildes[0]); /* Finished with pipe */
exit(EXIT_SUCCESS);
default: /* Parent - writes to pipe */
close(fildes[0]); /* Read end is unused */
write(fildes[1], "Hello world\n", 12); /* Write data on pipe */
close(fildes[1]); /* Child will see EOF */
exit(EXIT_SUCCESS);
}
我想知道父级使用管道与其子级通信如何确保不会发生子级在父级运行 write 之前运行 read() 的情况,和孩子 在父级完成写入之前完成读取?
我想知道在父级中创建的管道是否可以用于 父母和孩子之间的双向沟通,或者只是一种方式 父母对孩子不是相反吗?
如果子进程可以通过管道向父进程发送数据,那么会发生什么? 程序看起来像?
是一个像真正的管道一样有两端的管道。 fildes[0] 和 fildes[1] 分别用来表示两端?
如果管道是双向的,读端和写端是什么意思 相对于,即哪个读取(写入),父级还是子级?
谢谢和问候!
From http://pubs.opengroup.org/onlinepubs/009604599/functions/pipe.html:
The pipe() function shall create a pipe and place two file
descriptors, one each into the arguments fildes[0] and fildes[1], that
refer to the open file descriptions for the read and write ends of the
pipe.
There is an example where a parent writes data to its child:
int fildes[2];
const int BSIZE = 100;
char buf[BSIZE];
ssize_t nbytes;
int status;
status = pipe(fildes);
if (status == -1 ) {
/* an error occurred */
...
}
switch (fork()) {
case -1: /* Handle error */
break;
case 0: /* Child - reads from pipe */
close(fildes[1]); /* Write end is unused */
nbytes = read(fildes[0], buf, BSIZE); /* Get data from pipe */
/* At this point, a further read would see end of file ... */
close(fildes[0]); /* Finished with pipe */
exit(EXIT_SUCCESS);
default: /* Parent - writes to pipe */
close(fildes[0]); /* Read end is unused */
write(fildes[1], "Hello world\n", 12); /* Write data on pipe */
close(fildes[1]); /* Child will see EOF */
exit(EXIT_SUCCESS);
}
I was wondering how a parent using a pipe to communicate with its child can make sure it will not happen that the child runs read() before the parent runs write, and the child
finish reading before the parent finish writing?I was wondering if a pipe created in the parent can be used for
two-way communication between parent and child, or just one way from
the parent to the child not the other way?If the child can send data to parent via the pipe, what will the
program looks like?Is a pipe like a real pipe with two ends. fildes[0] and fildes[1]
are used to represent the two ends respectively?If a pipe is two way, what is the meaning of read end and write end
with respect to,i.e.which one read (write), the parent or the child?
Thanks and regards!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
O_NONBLOCK
,否则read
将阻塞并等待,直到数据可用。所以这并不重要;如果数据尚不可用,它将等待。fildes[0]
始终是读取(输出)端,fildes[1]
始终是写入(输入)结束。因此,您完全按照您所做的那样设置一个管道(其中父级获取写入端,子级获取读取端,因此父级可以发送给子级),然后以相反的方式设置另一个管道(你让父母保留阅读端并给孩子书写端)。fildes[0]
和fildes[1]
确实代表管道的末端,但它们不可互换。一种用于将数据“放入”管道,另一种用于将数据“从”管道“取出”。如果您需要更多说明,请随时询问;我意识到这可能会让新手感到困惑。
更新以回答评论中的问题:
pipe()
,并且其子进程将继承它接收到的文件描述符(除非您关闭它们)。也就是说,它们可以通过多次fork()
调用而存活下来。因此,如果您愿意,您可以编写一个程序,将文件描述符提供给孙子、曾孙等。pipe()
来创建该对(如果需要双向通信,则创建两个对),然后分叉两次。然后,您可以让一个孩子使用fildes[0]
,另一个孩子使用fildes[1]
。父级不应使用这两个文件描述符。瞧!与共同父母的孩子之间的沟通。值得注意的是,这个简单(尽管可能不直观)的功能有多么强大。这看起来类似于下面的示例。fork()
想象成一台克隆机。在进入克隆机之前,您使用pipe()
制作一对使用特殊秘密频率的匹配对讲机。一个只能传送;另一个只能接收。当您使用克隆机时,您会出现两个副本(原始副本,即父副本,和克隆副本,即子副本)。现在,每个副本都携带一对相同的匹配对讲机。如果原人摧毁了他的“接收”设备,而克隆人摧毁了他的“传输”设备,那么原人可以使用对讲机与克隆人交谈(但反之则不然)。但是你看,这里的关键思想不是
pipe()
连接两个进程(尽管这是它的主要用途),而是它是在期间重复的东西。 fork()。你可以多次重新进入克隆机并得到很多对讲机,所以定义“谁在和谁说话”(哪些进程通过管道连接)并不是克隆机或克隆机功能的特定方面。对讲机。
相反,它取决于谁最终实际使用哪些端点,这完全由您控制。如果您愿意,您可以让孩子关闭两个端点(销毁两个对讲机),这会让父母自言自语(这很愚蠢,可能没用,但这是可能的) 。我意识到这有点离题,但我试图传达基本思想是如何工作的,以便您了解管道可能实现的事物领域。
兄弟进程与管道通信的示例(单向):
O_NONBLOCK
,read
will block and wait until data becomes available. So it doesn't matter; if data isn't available yet, it will wait for it.fildes[0]
is always the reading (output) end, andfildes[1]
is the writing (input) end. So you set up one pipe exactly as you have done it (where the parent gets the writing end and the child gets the reading end, so the parent can send to the child), and you set up the other pipe in the opposite manner (you let the parent keep the reading end and give the child the writing end).fildes[0]
andfildes[1]
do indeed represent the ends of the pipe, but they are not interchangeable. One is used for putting data "into" the pipe, and the other is for getting data "out of" the pipe.If you need more clarification, feel free to ask; I realize this can be confusing for newcomers.
Update to answer questions in comment:
pipe()
, and its subprocesses will inherit the file descriptors it receives (unless you close them). That is, they survive through multiplefork()
calls. So you can write a program that will give the file descriptors to grandchildren, great-grandchildren, etc. if you want to.pipe()
to create the pair (or two pairs if you want bidirectional communication), then forks twice. You can then have one child usefildes[0]
, and the other child usefildes[1]
. The parent should use neither file descriptor. Voilà! Communication between children with a common parent. It's remarkable how powerful this simple (albeit potentially unintuitive) feature is. This looks something like the example below.fork()
as a cloning machine. Before going into the cloning machine, you usepipe()
to make a pair of matched walkie-talkies which use a special secret frequency. One can only transmit; the other can only receive. When you use the cloning machine, two copies of you emerge (the original, i.e. parent, and the clone, i.e. child). Each copy is now carrying an identical pair of matched walkie-talkies. If the original destroys his "receive" device and the clone destroys his "transmit" device, then the original can talk to the clone using the walkie-talkies (but not the other way around).But you see, the key idea here is not that
pipe()
connects two processes (though that's its primary use), it's that it's something that is duplicated during thefork()
. You can re-enter the cloning machine many times and get many walkie-talkies, so what defines "who is talking to whom" (which processes are connected by the pipe) is not a particular aspect of the function of the cloning machine or the walkie-talkies.Rather, it's determined by who ends up actually using which endpoints, which is entirely in your control. If you wanted, you could have the child close both endpoints (destroy both walkie-talkies), which would leave the parent to talk to himself (this is silly and probably not useful, but it is possible). I realize this was a bit of a tangent, but I'm trying to communicate how the basic idea works so that you understand the realm of things that are possible with pipes.
Example of sibling processes talking with a pipe (unidirectional):
如果子进程运行
read()
,只要没有为描述符设置不阻塞,它就会阻塞,直到数据可用。如果子进程完成读取并关闭管道,父进程将收到SIGPIPE
信号,并且write()
将中止。管道通常是单向通信。有(传闻)双向管道的实现,但我从未遇到过其中之一。
正如2.中所述,管道最初被定义为单向的,因此父管道有写入的一端,而子管道有读取的一端。如果双向管道可用,我猜测子进程可以写入其末尾,而父进程可以从其末尾读取。
也许
man 7 pipeline
也会对你有帮助。If the child runs
read()
, it will block until data is available, as long as no no blocking is set for the descriptor. If the child finishes reading and closes the pipe, the parent will get aSIGPIPE
signal andwrite()
will abort.A pipe usually is unidirectional communication. There are (rumors), implementations of bidirectional pipes, but I never encountered one of them.
As said in 2., pipes originally were defined as unidirectional, so the parent has the end where to write to, and the child the end where to read from. In case bidirectional pipes are available, I guess that the child can write into its end and the parent read from its end.
Maybe
man 7 pipe
will help you as well.