pipe用法與範例

pipe是linux的system call
是用來做linux IPC(Inter-Process Communication)中一個叫做管道(pipe)的函數

[目錄]
    pipe規格與格式
    pipe範例


[pipe規格與格式]

語法:
        int pipe(int pipefd[2]);

標頭檔:
        #include <unistd.h>

回傳值:
         0:pipe建立成功
        -1:pipe建立失敗

注意事項:
        1. 當所有正在運行的process中,此pipe的read end都關閉了,則此pipe關閉
        2. 若寫入一個read end已經關閉的pipe,則調用此write()的process會得到
            一個SIGPIPE,會終止程式。若忽略此signal繼續寫入會得到error叫做EPIPE
        3. 若讀取一個write end已經關閉的pipe,則會得到EOF
        4. pipe的write end傳完畢後,會傳送EOF給read end
            我實驗的結果是,只有含有write end的process終止時才會傳送EOF
            並不是傳完一串字串後就會傳送EOF
        5. 若pipe的read end接收到EOF,就會關閉此read end
        6. 所有沒有用到的read end、write end一定要關掉,不然process會無法終止
            而當沒用到的end都關掉之後,此pipe的關閉時機就靠上述4.和5.兩點決定


[pipe範例]
三個範例
1. 一個pipe兩個process單向傳輸
2. 兩個pipe兩個process雙向傳輸
3. 兩個pipe重複利用於無窮個process
以下一一展示

1. 一個pipe兩個process單向傳輸
//pipe_simple.cpp
#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>

int main(){

        int p1[2];
        
        if(pipe(p1)<0)
                std::cout << "pipe1 create error" << std::endl;
        signal(SIGCHLD,SIG_IGN);
        if(fork() == 0){ /*child process*/
                std::cout << "this is child process" << std::endl;
                close(p1[1]);
                dup2(p1[0], STDIN_FILENO); /*p1 will close after STDIN receive EOF*/
                close(p1[0]);
                execlp("cat","cat",NULL);
                exit(0);
        }else{ /*parent process*/
                std::cout << "this is parent process" << std::endl;
                close(p1[0]);
                dup2(p1[1], STDOUT_FILENO);
                close(p1[1]);
                std::cout << "can u hear me?" << std::flush;
        }
}
result : 
會有這樣的結果是parent process先跑完,child process才跑完



2. 兩個pipe兩個process雙向傳輸
這個範例把一個字串用一個pipe傳過去再用另一個pipe傳回來
//pipe_simple.cpp
#include <iostream>
#include <unistd.h>
#include <cstring>
#include <signal.h>
#include <stdlib.h>

int main(){

        int p1[2];
        int p2[2];
        int num = 0;
        int stdout_copy = dup(STDOUT_FILENO);
        char pipe_buff[10000] = {0};

        if(pipe(p1)<0)
                std::cout << "pipe1 create error" << std::endl;
        if(pipe(p2)<0)
                std::cout << "pipe2 create error" << std::endl;
        signal(SIGCHLD,SIG_IGN);
        if(fork() == 0){ /*child process*/
                std::cout << "this is child process" << std::endl;
                close(p1[1]);
                dup2(p1[0], STDIN_FILENO); /*p1 will close after STDIN receive EOF*/
                close(p1[0]);
                close(p2[0]);
                dup2(p2[1], STDOUT_FILENO);
                close(p2[1]);
                execlp("cat","cat",NULL);
                exit(0);
        }else{ /*parent process*/
                std::cout << "this is parent process" << std::endl;
                close(p1[0]);
                dup2(p1[1], STDOUT_FILENO);
                close(p1[1]);
                std::cout << "can u hear me?" << std::flush;
                close(p2[1]);
                dup2(stdout_copy,STDOUT_FILENO); /*p1 write end isn't used anymore, send EOF*/
                memset(pipe_buff, 0, sizeof(pipe_buff));
                num = read(p2[0],pipe_buff,sizeof(pipe_buff));
                std::cout << "num: " << num << std::endl
                          << "pipe_buff: " << pipe_buff << std::endl;
                close(p2[0]); /*p2 close here*/
        }
}
result :



3. 兩個pipe重複利用於無窮個process
//pipe_test.cpp
#include <unistd.h>
#include <iostream>
#include <string>
#include <cstring>
#include <sstream>
#include <signal.h>
#include <vector>
#include <algorithm>
#include <sys/wait.h>

#define MAX_WORDS_IN_LINE 1000
#define BUFF_SIZE 100000

int main(){

        int p1[2];
        int p2[2];
        int num = 0;
        int this_vbar = 0;
        int last_vbar = 0;
        int stdin_copy = dup(STDIN_FILENO);
        int stdout_copy = dup(STDOUT_FILENO);
        char pipe_buff[BUFF_SIZE] = {0};
        std::string str_in = "";
        std::string str_temp = "";
        std::vector<std::string> cmd;
        std::vector<int> vbar_pos;
        std::stringstream ss;

        /*get input from std::cin, and store it*/
        std::getline(std::cin,str_in,'\n');
        ss.str(str_in);
        vbar_pos.push_back(-1);
        for(int i=0; ss>>str_temp; i++){
                cmd.push_back(str_temp);          /*store splited strings in vector cmd*/
                if(cmd.at(i) == "|"){
                        vbar_pos.push_back(i);   /*store the pos of "|" in vector vbar_pos*/
                }
        }

        /*start doing the pipe task*/
        for(int i=0; i<cmd.size(); i++){
                if(cmd.at(i) == "|" || i==cmd.size()-1){
                        if(cmd.at(i) == "|"){
                                this_vbar = std::find(vbar_pos.begin(), vbar_pos.end(), i) - vbar_pos.begin();
                                last_vbar = this_vbar-1;  /*this_vbar, last_vbar are indexes in vbar_pos*/
                        }
                        if(pipe(p1)<0)
                                std::cout << "create pipe1 error" << std::endl;
                        if(pipe(p2)<0)
                                std::cout << "create pipe2 error" << std::endl;
                        signal(SIGCHLD,SIG_IGN);
                        if(fork()==0){ /*child process: call execvp if cmd.at(i) is "|" or is the last word*/
                                if(i==cmd.size()-1){
                                        close(p1[1]);
                                        dup2(p1[0],STDIN_FILENO); /*p1 will close after STDIN receive EOF*/
                                        close(p1[0]);
                                        close(p2[0]);
                                        close(p2[1]);
                                        dup2(stdout_copy,STDOUT_FILENO);
                                        char* arg[MAX_WORDS_IN_LINE] = {0};
                                        for(int j=0 ; j<(i-vbar_pos.at(this_vbar)); j++){
                                                arg[j] = strdup(cmd.at(vbar_pos.at(this_vbar)+j+1).c_str());
                                        }
                                        arg[i-vbar_pos.at(this_vbar)] = NULL;
                                        execvp(arg[0],arg);
                                        exit(0);
                                }else{
                                        close(p1[1]);
                                        dup2(p1[0],STDIN_FILENO); /*p1 will close after STDIN receive EOF*/
                                        close(p1[0]);
                                        close(p2[0]);
                                        dup2(p2[1],STDOUT_FILENO);
                                        close(p2[1]);
                                        char* arg[MAX_WORDS_IN_LINE] = {0};
                                        for(int j=0 ; j<(i-vbar_pos.at(last_vbar)-1); j++){
                                                arg[j] = strdup(cmd.at(vbar_pos.at(last_vbar)+j+1).c_str());
                                        }
                                        arg[i-vbar_pos.at(last_vbar)-1] = NULL;
                                        execvp(arg[0],arg);
                                        exit(0);
                                }
                        }else{ /*parent process: pass old data to the new child and store new data*/
                                close(p1[0]);
                                dup2(p1[1],STDOUT_FILENO);
                                close(p1[1]);
                                std::cout << pipe_buff << std::flush;
                                dup2(stdout_copy,STDOUT_FILENO); /*p1 write end isn't used anymore, send EOF*/
                                close(p2[1]);
                                if(i<cmd.size()-1){
                                        memset(pipe_buff, 0, sizeof(pipe_buff));
                                        read(p2[0],pipe_buff, sizeof(pipe_buff));
                                }
                                close(p2[0]);  /*p2 close here*/
                        }
                }
        }
}
result :
會有這樣的結果是parent process先跑完,child process才跑完


好了,今天的筆記到此結束
希望有幫助未來遺忘這些的自己,以及需要的人

留言

這個網誌中的熱門文章