2021-05-12 14:32:11
Linux進程間通訊-命名管道深入理解
繼上篇文章分析了進程間通訊管道的機制和特性,本文將從命名管道(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
相關文章