通过管道实现进程间通信

5
众所周知,在Linux中的进程间通信过程中,进程们通过一个名为“Pipe”的特殊文件进行通信。
众所周知,该文件上执行的操作由一个进程执行写入操作,另一个进程执行读取操作以进行通信。
现在问题是:
这些写入读取操作在通信过程中是否同时执行(即操作并行执行)?如果不是,则会发生什么?
当其中一个进程在通信过程中进入SLEEP状态时会发生什么?它会首先执行写操作以便第二个进程可以读取吗?还是直接进入睡眠状态而不执行任何的操作?
1个回答

3

发送进程可以一直写入,直到管道缓冲区满(Linux 上为 64k,自 2.6.11 起)。在此之后,write(2) 将被阻塞。

接收进程将阻塞,直到 read(2) 有可用数据。

如需更详细了解管道缓冲,请查看 https://unix.stackexchange.com/a/11954

例如,这个程序

#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int
main(int argc, char *argv[])
{
    int pipefd[2];
    pid_t cpid;
    char wbuf[32768];
    char buf[16384];

    /* Initialize writer buffer with 012...89 sequence */
    for (int i = 0; i < sizeof(wbuf); i++)
      wbuf[i] = '0' + i % 10;

    if (pipe(pipefd) == -1) {
        perror("pipe");
        exit(EXIT_FAILURE);
    }

    cpid = fork();
    if (cpid == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    }

    if (cpid == 0) {    /* Child reads from pipe */
        close(pipefd[1]);          /* Close unused write end */
        while (read(pipefd[0], &buf, sizeof(buf)) > 0);
        close(pipefd[0]);
        _exit(EXIT_SUCCESS);

    } else {            /* Parent writes sequence to pipe */
        close(pipefd[0]);          /* Close unused read end */
        for (int i = 0; i < 5; i++)
          write(pipefd[1], wbuf, sizeof(wbuf));
        close(pipefd[1]);          /* Reader will see EOF */
        wait(NULL);                /* Wait for child */
        exit(EXIT_SUCCESS);
    }
}

使用gcc pipes.c && strace -e trace=open,close,read,write,pipe,clone -f ./a.out运行将产生以下序列:

open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
close(3)                                = 0
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\320\3\2\0\0\0\0\0"..., 832) = 832
close(3)                                = 0
pipe([3, 4])                            = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f32117489d0) = 21114
close(3)                                = 0
write(4, "01234567890123456789012345678901"..., 32768) = 32768
write(4, "01234567890123456789012345678901"..., 32768) = 32768
write(4, "01234567890123456789012345678901"..., 32768strace: Process 21114 attached
 <unfinished ...>
[pid 21114] close(4)                    = 0
[pid 21114] read(3, "01234567890123456789012345678901"..., 16384) = 16384
[pid 21114] read(3,  <unfinished ...>
[pid 21113] <... write resumed> )       = 32768
[pid 21114] <... read resumed> "45678901234567890123456789012345"..., 16384) = 16384
[pid 21113] write(4, "01234567890123456789012345678901"..., 32768 <unfinished ...>
[pid 21114] read(3, "01234567890123456789012345678901"..., 16384) = 16384
[pid 21114] read(3,  <unfinished ...>
[pid 21113] <... write resumed> )       = 32768
[pid 21114] <... read resumed> "45678901234567890123456789012345"..., 16384) = 16384
[pid 21113] write(4, "01234567890123456789012345678901"..., 32768 <unfinished ...>
[pid 21114] read(3, "01234567890123456789012345678901"..., 16384) = 16384
[pid 21114] read(3,  <unfinished ...>
[pid 21113] <... write resumed> )       = 32768
[pid 21114] <... read resumed> "45678901234567890123456789012345"..., 16384) = 16384
[pid 21113] close(4)                    = 0
[pid 21114] read(3, "01234567890123456789012345678901"..., 16384) = 16384
[pid 21114] read(3, "45678901234567890123456789012345"..., 16384) = 16384
[pid 21114] read(3, "01234567890123456789012345678901"..., 16384) = 16384
[pid 21114] read(3, "45678901234567890123456789012345"..., 16384) = 16384
[pid 21114] read(3, "", 16384)          = 0
[pid 21114] close(3)                    = 0
[pid 21114] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=21114, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++

你会注意到读写操作是交错进行的,而且写入和读取过程会因为管道已满或者没有足够的数据可供读取而阻塞几次。


这些操作是并行发生的吗? - HarshitMadhav
管道上的读写操作可以并发进行,但是read(2)仅返回已完成写操作的字节。鉴于少于PIPE_BUF字节的写入是原子的,因此读取器只会在写入完成后返回字节。有关更多信息,请参见pipe(7)。 - Gerd Flaig
1
pipe_read和pipe_write都会在管道上获取互斥锁,因此不能并行运行。请参见http://lxr.free-electrons.com/source/fs/pipe.c#L250和http://lxr.free-electrons.com/source/fs/pipe.c#L357。 - Gerd Flaig
我已经添加了一个带有系统调用跟踪输出的代码示例,以便澄清。 - Gerd Flaig
好的,你的意思是在进程间通信期间,如果一个进程进入睡眠状态,那么它们之间通过管道的通信将停止,并在另一个进程唤醒时恢复? - HarshitMadhav
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接