【问题标题】:Interprocess communication via Pipes通过管道进行进程间通信
【发布时间】:2017-09-10 18:13:39
【问题描述】:

众所周知,在Linux的进程间通信过程中,进程之间通过一个名为“管道”的特殊文件进行通信。

众所周知,对该文件执行的操作是由一个进程写入和一个进程读取以相互通信。

现在,问题是:

这些 writeread 操作是否在通信期间并行执行(操作并行执行)?如果不是,

当其中一个进程在通信过程中进入SLEEP状态时会发生什么?它是先执行 write 操作以让第二个进程 read 还是直接进入睡眠状态而不执行任何 write读取操作?

【问题讨论】:

    标签: linux process operating-system ipc interprocess


    【解决方案1】:

    发送进程可以写入直到管道缓冲区已满(自 2.6.11 起在 Linux 上为 64k)。之后,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 &amp;&amp; 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 +++
    

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

    【讨论】:

    • 这些操作是否并行发生?
    • 管道上的读取和写入操作可以是并发的,但是 read(2) 只会返回已完成写入操作的字节。鉴于小于 PIPE_BUF 字节的写入是原子的,读取器将仅在写入完成后返回字节。有关更多信息,请参见管道 (7)。
    • 你能解释清楚一点吗?我想这不是我问的。
    • pipe_read 和 pipe_write 都在管道上获取互斥体,因此不能并行运行。见lxr.free-electrons.com/source/fs/pipe.c#L250lxr.free-electrons.com/source/fs/pipe.c#L357
    • 除非你正在运行一个 CONFIG_PREEMPT=y 的内核,否则 write 调用在完成(如果有足够的缓冲区空间)或阻塞(这将强制进程进入睡眠)之前不能被抢占)。如果您想要更具体的答案,请考虑使用具有特定事件顺序的每个场景的时序图更新您的问题。
    猜你喜欢
    • 2011-12-22
    • 1970-01-01
    • 2022-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-26
    • 1970-01-01
    相关资源
    最近更新 更多