首頁 > 軟體

Linux進程間通訊-命名管道深入理解

2020-06-16 16:53:34

繼上篇文章分析了進程間通訊管道的機制和特性,本文將從命名管道(FIFO)介紹進程間通訊。

1、命名管道(FIFO)

管道應用的一個重大限制是它沒有名字,只適合具有親緣性質的進程之間通訊。命名管道克服了這種限制,FIFO不同於管道之處在於它提供一個路徑名與之關聯,以FIFO的檔案形式存在於檔案系統中。這樣,即使與FIFO的建立進程不存在親緣關係的進程,只要可以存取該路徑,就能夠彼此通過FIFO相互通訊(能夠存取該路徑的進程以及FIFO的建立進程之間),因此,通過FIFO不相關的進程也能交換資料。

2、建立一個命名管道

#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);

3、操作命名管道

FIFO在檔案系統中表現為一個檔案,大部分的系統檔案呼叫都可以用在FIFO上面,比如:read,open,write,close,unlink,stat等函數。但是seek等函數不能對FIFO呼叫。

可以呼叫open函數開啟命名管道,但是有兩點要注意
1)不能以O_RDWR模式開啟命名管道FIFO檔案,否則其行為是未定義的,管道是單向的,不能同時讀寫;
2)就是傳遞給open呼叫的是FIFO的路徑名,而不是正常的檔案

開啟FIFO檔案通常有四種方式:
open(pathname, O_RDONLY);           //1唯讀、阻塞模式
open(pathname, O_RDONLY | O_NONBLOCK);    //2唯讀、非阻塞模式
open(pathname, O_WRONLY);          //3只寫、阻塞模式
open(pathname, O_WRONLY | O_NONBLOCK);   //只寫、非阻塞模式

注意阻塞模式open開啟FIFO:
1)當以阻塞、唯讀模式開啟FIFO檔案時,將會阻塞,直到其他進程以寫方式開啟存取檔案;
2)當以阻塞、只寫模式開啟FIFO檔案時,將會阻塞,直到其他進程以讀方式開啟檔案;
3)當以非阻塞方式(指定O_NONBLOCK)方式唯讀開啟FIFO的時候,則立即返回。當只寫open時,如果沒有進程為讀開啟FIFO,則返回-1,其errno是ENXIO。

 4、阻塞式命名管道

Read進程程式碼如下:

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

int main(int argc, char* argv[])
{
    int ret = mkfifo("my_fifo",0777);
    if (ret == -1)
    {
        printf("make fifo failed!n");
        return 1;
    }

    char buf[256] = {0};
    int fd = open("my_fifo",O_RDONLY);
    read(fd,buf,256);
    printf("%sn",buf);
    close(fd);
    unlink("my_fifo");
    return 0;
}

Write進程程式碼如下:

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[])
{
    char *buf = "i am write processn";
    int fd = open("my_fifo",O_WRONLY);
    write(fd,buf,strlen(buf));
    close(fd);
    return 0;
}

首先啟動read進程,建立fifo,當前目錄下會生出一個名字為“my_fifo”的檔案,然後啟動write進程,執行結果如下:

結論:當write進程沒有以O_WRONLY模式open命名管道時,read進程在以O_RDONLY模式open命名管道的時候會阻塞。反過來也是這樣。

5、非阻塞式命名管道

Read進程程式碼入下:

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

int main(int argc, char* argv[])
{
    int ret = mkfifo("my_fifo",0777);
    if (ret == -1)
    {
        printf("make fifo failed!n");
        return 1;
    }

    char buf[256] = {0};
    int fd = open("my_fifo",O_RDONLY | O_NONBLOCK);
   // sleep(5);  
    read(fd,buf,256);
    printf("%sn",buf);
    close(fd);
    unlink("my_fifo");
    return 0;
}

Write進程程式碼如下:

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[])
{
    char *buf = "i am write processn";
    int fd = open("my_fifo",O_WRONLY | O_NONBLOCK);
    write(fd,buf,strlen(buf));
    close(fd);
    return 0;
}

直接啟動Read進程,open函數將不會阻塞,read函數讀取到0個位元組資料,程式退出。

將sleep(5)的註釋去掉,再次啟動Read進程,馬上啟動Write進程,Read進程會讀取到Write進程寫入fifo的資料並列印,然後退出。

結論:flags=O_RDONLY|O_NONBLOCK:如果此時沒有其他進程以寫的方式開啟FIFO,open也會成功返回,此時FIFO被讀開啟,而不會返回錯誤。

經測試:flags=O_WRONLY|O_NONBLOCK:立即返回,如果此時沒有其他進程以讀的方式開啟,open會失敗開啟,此時FIFO沒有被開啟,返回-1。

一、進程間通訊-管道 https://www.linuxidc.com/Linux/2018-04/151680.htm

二、進程間通訊-命名管道 https://www.linuxidc.com/Linux/2018-04/151681.htm

三、進程間通訊-訊息佇列 https://www.linuxidc.com/Linux/2018-04/151682.htm

四、進程間通訊-共用記憶體 https://www.linuxidc.com/Linux/2018-04/151683.htm

本文永久更新連結地址https://www.linuxidc.com/Linux/2018-04/151681.htm


IT145.com E-mail:sddin#qq.com