Skip to content
On this page

Linux下进程间通信方式——pipe(管道)

先看这一篇文章

Linux下进程间通信方式——pipe(管道) - cs_wu - 博客园

结合<<understanding unix/linux programming>>这本书的第10章:IO重定向与管道,我希望写出如下的代码,

  • popen,运行子进程,并与子进行进行通信
  • 向子进程写入数据,对他进行读取
  • 得到子进程的输出信息,与error信息

前置的知识

linux api

pipe
dup2
fcntl
eecvp
1
2
3
4

管道(pipe)读写的4种情况

plaintext

 write    +---------+   read
 -------->|  Pipe   |--------->
          +---------+
1
2
3
4
  1. read端如果一直在读,write端不写,会阻塞
  2. write端如果一直在写,read端不读,会阻塞
  3. read端如果一直在读,write端写了一部分,然后关闭了,read会读取到EOF,相当于文件的末尾
  4. write端如果一直在写,read端读了一部分,然后关闭了,write端会得到SIGPIPE信号

代码

注意看相应的代码

  1. 利用execvp调用另一个程序
sample.cpp
cpp
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <wait.h>

using T = char * const;
char * __argv[100] = {
    "ls","-l",NULL
};

int wait_child_exit(int pid){
    int status;
    int ret = -1;
    while ( 1 ) {
        ret = waitpid(pid, &status, WNOHANG);
        if( ret == -1) break;
        if( ret == 0) continue;
        return pid;
    }
    return ret;
}

int main(){
    int input_pipe_fd[2]; //写管道
    int output_pipe_fd[2];//读管道
    int ret = pipe(input_pipe_fd);
    if( ret == -1){
        std::cerr << "input_pipe_fd create failed" << "\n";
        return -1;
    }

    ret = pipe(output_pipe_fd);
    if( ret == -1){
        std::cerr << "output_pipe_fd create failed" << "\n";
        return -1;
    }

    int pid = fork();

    auto set_fd_no_close_on_exec = [](int fd){
        int flags =  fcntl(fd,F_GETFD,0);
        fcntl(fd, F_SETFD,flags & (~FD_CLOEXEC));
    };

    if(pid == 0){ // child
        close(output_pipe_fd[0]); //关闭 output的 读端
        close(input_pipe_fd[1]); //关闭 intput的 写端

        // dup2(input_pipe_fd[0], 0);
        dup2(output_pipe_fd[1], 1); //重定向写
        set_fd_no_close_on_exec(input_pipe_fd[0]);
        set_fd_no_close_on_exec(0);
        dup2(output_pipe_fd[1], 1);
        set_fd_no_close_on_exec(output_pipe_fd[1]);
        set_fd_no_close_on_exec(1);

        //执行
        execvp("ls",__argv);

    }
    else { // parent
        close(output_pipe_fd[1]); //关闭 output的 写端
        close(input_pipe_fd[0]); //关闭 intput的 读端
        
        //读取数据
        char buf[128];

        while ( 1 ) {
            
            int readn = read(output_pipe_fd[0], buf, sizeof(buf));
            if(readn <= 0) break;
            for(int i=1;i<=readn;++i){
                std::cout << buf[i-1];
            }
        }

        int ret = wait_child_exit(pid);
        std::cout << "wait child exit ret id: "
            << ret
            << "\n";
        std::cout << "\n main process exit " << "\n";

    }

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
  1. 测试通信之间的时间间隔问题
sample_1.cpp
cpp
// delay
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <fcntl.h>
#include <wait.h>

using T = char * const;
char * __argv[100] = {
    "ls","-l",NULL
};

int wait_child_exit(int pid){
    int status;
    int ret = -1;
    while ( 1 ) {
        ret = waitpid(pid, &status, WNOHANG);
        if( ret == -1) break;
        if( ret == 0) continue;
        return pid;
    }
    return ret;
}

int main(){
    int input_pipe_fd[2]; //写管道
    int output_pipe_fd[2];//读管道
    int ret = pipe(input_pipe_fd);
    if( ret == -1){
        std::cerr << "input_pipe_fd create failed" << "\n";
        return -1;
    }

    ret = pipe(output_pipe_fd);
    if( ret == -1){
        std::cerr << "output_pipe_fd create failed" << "\n";
        return -1;
    }

    int pid = fork();

    auto set_fd_no_close_on_exec = [](int fd){
        int flags =  fcntl(fd,F_GETFD,0);
        fcntl(fd, F_SETFD,flags & (~FD_CLOEXEC));
    };

    if(pid == 0){ // child
        close(output_pipe_fd[0]); //关闭 output的 读端
        close(input_pipe_fd[1]); //关闭 intput的 写端

        // dup2(input_pipe_fd[0], 0);
        dup2(output_pipe_fd[1], 1); //重定向写
        set_fd_no_close_on_exec(input_pipe_fd[0]);
        set_fd_no_close_on_exec(0);
        dup2(output_pipe_fd[1], 1);
        set_fd_no_close_on_exec(output_pipe_fd[1]);
        set_fd_no_close_on_exec(1);

        //执行
        const char * child_message = "hello world !";
        for(int i=1;i<=3;++i){

            // std::cout << "print " << i << " times" << std::endl;
            // std::endl 会把c++ 输出缓存的内容fflush

            write(1,child_message , strlen(child_message));
            sleep(1);
        }
        // https://unix.stackexchange.com/a/430371
        // fd will automatically closed when process terminates
        return 0;

    }
    else { // parent
        close(output_pipe_fd[1]); //关闭 output的 写端
        close(input_pipe_fd[0]); //关闭 intput的 读端
        
        //读取数据
        char buf[128];

        char message_buf[128];
        while ( 1 ) {
            
            int readn = read(output_pipe_fd[0], buf, sizeof(buf));
            //std::cout << "\n===> readn bytes : " << readn << "\n";
            sprintf(message_buf, "\n===> readn %d bytes.\n",readn);
            write(1, message_buf, strlen(message_buf));
            if(readn <= 0) break;
            // for(int i=1;i<=readn;++i){
            //     std::cout << buf[i-1];
            // }
            write(1, buf, readn);
        }

        int ret = wait_child_exit(pid);
        std::cout << "wait child exit ret id: "
            << ret
            << "\n";
        std::cout << "\n main process exit " << "\n";

    }

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104