加入星計劃,您可以享受以下權益:

  • 創(chuàng)作內容快速變現(xiàn)
  • 行業(yè)影響力擴散
  • 作品版權保護
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
  • 相關推薦
  • 電子產業(yè)圖譜
申請入駐 產業(yè)圖譜

Linux進程通信之管道解析

2021/07/07
462
閱讀需 15 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

概述

管道是 UNIX系統(tǒng) IPC的最古老的形式,所有的UNIX系統(tǒng)都提供此種通信。所謂的管道,也就是內核里面的一串緩存,從管道的一段寫入的數(shù)據(jù),實際上是緩存在內核中的,令一端讀取,也就是從內核中讀取這段數(shù)據(jù)。對于管道傳輸?shù)臄?shù)據(jù)是無格式的流且大小受限。對于管道來說,也分為匿名管道和命名管道,其中命名管道也被叫做 FIFO,下面則分別闡述這兩種管道。

匿名管道

默認情況下,在 Shell命令執(zhí)行過程中,任何一個命令都有一個標準輸入設備(鍵盤)、標準輸出設備(顯示器)和標準輸出設備(顯示器),使用管道"|"可以將兩個命令連接起來,從而改變標準的輸入輸出方式,下面是在 Linux 端運行命令行的一個截圖:

image-20210704161819420

上述命令中的意思也就是,將ls命令得到的結果作為 grep tags命令的輸入。

image-20210704162803903

 

連接輸入輸出的中間設備即為一個管道文件,綜上,也就是說使用管道可以將一個命令的輸出作為另一個命令的輸入(在運行的時候,一個命令將創(chuàng)建一個進程),而這種管道是臨時的,命令執(zhí)行完畢之后就會自動消失,這類管道稱為無名管道。

匿名管道例子

匿名管道在使用前要先創(chuàng)建,其函數(shù)的聲明如下:

extern int pipe (int __pipedes[2]);

此函數(shù)的參數(shù)是一個整型數(shù)組,如果執(zhí)行成功,pipe 將存儲兩個整型文件描述符于__pipedes[0]__pipedes[1]中,他們分別指向管道的兩端。如果系統(tǒng)調用失敗,則返回 -1。

讀無名管道,該函數(shù)的聲明如下:

extern ssize_t read (int __fd, void *__buf, size_t __nbytes);

第一個參數(shù)fd為打開的文件描述符,buf為讀出數(shù)據(jù)的存儲位置,nbytes為讀取數(shù)據(jù)的大小,調用 read 函數(shù)將從 fd 指向的文件描述符指定的打開文件中宏讀 n 字節(jié)到 buf 指向的緩沖區(qū)內。

如果試圖向已經(jīng)填滿的管道寫入,系統(tǒng)會自動阻塞。一個管道不能同時被兩個進程打開。

extern ssize_ t write(int __fd, __const void *__buf, size_t __n);

從 buf指向的緩沖區(qū)中向管道中寫入nbytes字節(jié),且每次寫入的內容都附件在管道的末端。

那要如何使用管道在兩個進程之間通信呢,我們可以使用 fork()創(chuàng)建子進程,創(chuàng)建的子進程會復制父進程的文件描述符,這樣就做到了兩個進程各有兩個fd[0]與fd[1],兩個進程就可以通過各自的fd寫入和讀取同一個管道文件實現(xiàn)進程通信了,具體原理如下所示:

image-20210704170602297

 

具體的例子如下所示:

#include
#include
#include

int main(int argc, char *argv[])
{
    pid_t pid;
    int temp;
    int pipedes[2];
    char s[14] = "test message!";
    char d[14];

    if (pipe(pipedes) == -1) // 創(chuàng)建管道
    {
        perror("pipe");
        exit(EXIT_FAILURE);
    }

    if (pid == fork() == -1)
    {
        perror("fork");
        exit(EXIT_FAILURE);
    }
    else if (pid == 0)      // 子進程
    {
        printf("now,write data to pipen");
        if (write(pipedes[1], s, 14) == -1)   // 寫數(shù)據(jù)到管道
        {
            perror("write");
            exit(EXIT_FAILURE);
        }
        else
        {
            printf("the written data is:%sn",s);
            exit(EXIT_SUCESS);
        }
    }
    else if (pid > 0)     // 父進程
    {
        slepp(2);
        printf("now, read from pipen");
        if ((read(pipedes[0], d, 14)) == -1)
        {
            perror("read");
            exit(EXIT_FAILURE);
        }
        printf("the data from pipe is:%sn",d);
    }
    return 0;
}

代碼運行的結果如下所示:

image-20210704172243185

 

命名管道

命名管道又被稱之為是 FIFO ,未命名的管道只能在兩個相關的進程之間使用,而且這兩個相關的進程還要又一個共同創(chuàng)建了他們的祖先進程,但是,通過 FIFO ,不相關的進程也能交換數(shù)據(jù)。

首先,介紹下是如何創(chuàng)建命名管道的:

extern int mkfifo (__const char *__path, __mode_t __mode);

mkfifo會根據(jù)參數(shù)建立特殊的有名管道文件,該文件必須不存在,而參數(shù)mode為該文件的權限。

下面是一個使用命名管道進行進程間通信的例子,例子分為兩個程序,分別是讀部分和寫部分,首先看先往管道寫數(shù)據(jù)的代碼,代碼如下所示:

#include  
#include  
#include  
#include  
#include  
#include  

int main() 
{ 
    int fd; 

    // FIFO file path 
    char * myfifo = "/tmp/myfifo"; 

    // Creating the named file(FIFO) 
    // mkfifo() 
    mkfifo(myfifo, 0666); 

    char arr1[80], arr2[80]; 
    while (1) 
    { 
        // Open FIFO for write only 
        fd = open(myfifo, O_WRONLY); 
        printf("The fd is:%dn",fd);

        // Take an input arr2ing from user. 
        // 80 is maximum length 
        fgets(arr2, 80, stdin); 

        // Write the input arr2ing on FIFO 
        // and close it 
        write(fd, arr2, strlen(arr2)+1); 
        close(fd); 

        // Open FIFO for Read only 
        fd = open(myfifo, O_RDONLY); 

        // Read from FIFO 
        read(fd, arr1, sizeof(arr1)); 

        // Print the read message 
        printf("User2: %s", arr1); 
        close(fd); 
    } 
    return 0; 
}

然后是先往管道讀數(shù)據(jù)的代碼,代碼如下所示:

#include  
#include  
#include  
#include  
#include  
#include  

int main() 
{ 
    int fd1; 

    // FIFO file path 
    char * myfifo = "/tmp/myfifo"; 

    char str1[80], str2[80]; 
    while (1) 
    { 
        // First open in read only and read 
        fd1 = open(myfifo,O_RDONLY); 
        printf("The fd is:%dn",fd1);
        read(fd1, str1, 80); 

        // Print the read string and close 
        printf("User1: %s", str1); 
        close(fd1); 

        // Now open in write mode and write 
        // string taken from user. 
        fd1 = open(myfifo,O_WRONLY); 
        fgets(str2, 80, stdin); 
        write(fd1, str2, strlen(str2)+1); 
        close(fd1); 
    } 
    return 0; 
} 

下面是代碼運行的一個結果:

image-20210706132916572

 

說明一下,就是說當運行 write程序的時候,會創(chuàng)Linux建fifo文件,命名管道,然后,在 write文件中就執(zhí)行open操作,但是,這里存在的一個問題就是,因為在運行 write程序的時候,沒有進程打開讀端,也就阻塞了 open函數(shù)的運行,只有運行read操作,以讀的方式讀取管道的數(shù)據(jù),這樣才能使得write中的open函數(shù)繼續(xù)執(zhí)行。

綜上,也就是命名管道在進程中通信的一個例子。

小結

上述就是本次進程通信中關于管道的相關內容,其中就包括匿名管道以及命名管道,他們之間存在著差別嗎,也各有各的應用,本次的分享就到這里啦~

相關推薦

電子產業(yè)圖譜

在讀碩士研究生,喜歡鉆研嵌入式相關技術,熱衷于寫文章分享知識,不定期輸出關于單片機, RTOS,信號處理等相關內容