Linux進程間通信-匿名管道


前面我們講了進程間通信的一種方式,共享內存。下面看一看另一種機制,匿名管道。
1.什么是管道
管道是一個進程的數據流到另一個進程的通道,即一個進程的數據輸出作為另一個進程的數據輸入,管道起到了橋梁的作用。
比如,在shell中輸入命令:ls -l|grep string,ls和grep是兩個進程,"|"符號表示管道,意思是執行ls -l進程,並將輸出結果result_1,作為grep string進程的輸入result_0,grep進程將result_0中存在字符串string的信息打印到屏幕。

2.管道的使用
1)popen函數:啟用一個新進程,並可以向它傳遞數據,或者通過它接受數據。

FILE *popen(const char *command,conse char *open_mode);

command:運行的程序名和參數
open_mode:有兩個值"r(只讀)","w(只寫)"
      "r":可以獲取新進程的輸出
      "w":可以向新進程發送數據
返回值:返回輸入輸出文件流指針

2)pclose函數:關閉輸入輸出文件流指針
若調用該函數時,新進程仍然在運行,則pclose將等待,直至新進程結束。
返回值:返回新進程的退出碼。

3.popen函數使用示例
下例循環讀取read_fp輸出文件流的內容,寫入write_fp的輸入文件流,直到輸出流內容讀完。

#include<stdlib.h>
#include<stdio.h>
#include<string.h>
int main()
{
    FILE *read_fp = NULL;
    FILE *write_fp = NULL;
    char buffer[BUFSIZ+1];
    int chars_read = 0;

    //初始化緩沖區
    memset(buffer,'\0',sizeof(buffer));
    read_fp = popen("ls -l","r");
    write_fp = popen("grep rwxrwxr-x","w");
    if(read_fp && write_fp)
    {
        
        chars_read = fread(buffer,sizeof(char),BUFSIZ,read_fp);
        while(chars_read)
        {
            buffer[chars_read]='\0';
            //把數據寫入grep進程
            fwrite(buffer,sizeof(char),chars_read,write_fp);
            chars_read = fread(buffer,sizeof(char),BUFSIZ,read_fp);
        }
        //關閉文件流
        pclose(read_fp);
        pclose(write_fp);
        exit(EXIT_SUCCESS);
    }
    printf("%d\n",2);
    exit(EXIT_FAILURE);
}

輸出結果:

3、popen的原理及優缺點
當調用popen運行一個新進程時,它首先啟動shell,然后將command參數傳遞給它
優點:可以使用shell來分析命令字符串,啟動非常復雜的shell命令。
缺點:不僅要啟動一個新進程,還要啟動一個shell,效率會比較低。

4.pipe函數的使用

int pipe(int file_description[2]);

file_description[2]:表示管道的輸出輸入端,輸出端數據經過管道流到輸入端,函數執行完后, 會將這個數組賦值。
          file_description[1]表示管道輸出端文件描述符
          file_description[0]表示管道輸入端文件描述符
返回值:0成功,-1失敗

與popen不同的是,pipe函數是一個底層調用,不會啟動shell。
popen是使用文件流(FILE)工作的,pipe使用的是文件描述符,相應的數據要用底層的read和write來讀取和發送。

5.pipe函數使用示例
下例中,我們在父進程中創建一個管道,然后調用fork創建一個子進程。
此時,父進程的file_description[1]輸出端,對應着子進程file_description[0]的輸入端。
數據通過管道由父進程傳到子進程。示例如下:

#include<stdlib.h>
#include<stdio.h>
#include<string.h>
int main()
{
    int data_processed = 0;
    const char data[]="Hello pipe!";
    char buffer[BUFSIZ+1];
    pid_t pid;
    memset(buffer,'\0',sizeof(buffer));
    int filedes[2];
    if(pipe(filedes)==0)
    {
        //創建管道成功
        //fork子進程
        pid=fork();
        if(pid==-1)
        {
            fprintf(stderr,"Fork failure");
            exit(EXIT_FAILURE);
        }
        if(pid==0)
        {
            data_processed = read(filedes[0],buffer,BUFSIZ);
            printf("read %d bytes:%s\n",data_processed,buffer);
            exit(EXIT_SUCCESS);
        }
        else
        {
            data_processed = write(filedes[1],data,strlen(data));
            printf("wrote %d bytes:%s\n",data_processed,data);
            exit(EXIT_SUCCESS);
        }
    }
    exit(EXIT_FAILURE);
}

輸出結果:

6.管道用作標准輸入和輸出
我們知道標准的輸入描述符為0,輸出描述符為1,
為了使用已經定義好的標准程序,如od命令,從標准輸入讀入數據。
需要將管道的輸入端描述符置為0,此時,我們需要用到一個輔助函數dup

dup函數:創建一個描述符,復制原有描述符參數的結構到新建的描述符。

int dup(int file_descriptor);

新的描述符規則是,使用最小的可用值。

要想使管道的輸入描述符為標准輸入描述符,我們可以先關閉文件描述符0,然后調用dup,
此時新建的描述符即為最小可用值0,標准輸入描述符。

close(0);
dup(file_description[0]);

上例使用標准輸入描述符改造后的示例如下:

#include<stdlib.h>
#include<stdio.h>
#include<string.h>
int main()
{
    int data_processed = 0;
    const char data[]="Hello pipe!";
    int filedes[2];
    pid_t pid;
    if(pipe(filedes)==0)
    {
        pid = fork();
        if(pid==-1)
        {
            fprintf("stderr","fork failure!\n");
            exit(EXIT_FAILURE);
        }
        if(pid==0)
        {
            close(0);
            dup(filedes[0]);
            close(filedes[0]);
            close(filedes[1]);
            execlp("od","od","-c",0);
            exit(EXIT_FAILURE);
        }
        else
        {
            close(filedes[0]);
            data_processed = write(filedes[1],data,strlen(data));
            close(filedes[1]);
            printf("wrote %d bytes:%s\n",data_processed,data);
        }
    }
}

輸出結果:

7.匿名管道需要注意的問題
1)當管道沒有關閉時,若沒有數據可讀,read調用會阻塞
2)當管道關閉時,read調用會返回0
3)匿名管道通信,進程間必須是父子關系


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM