一、有名管道概念
上一節對無名管道做了闡述,無名管道隻能用在兩個有親緣關系的程序直接。而有名管道FIFO可以用在任意兩程序之間,它依賴于檔案系統,是一個存在的特殊檔案。
有名管道和普通檔案一樣具有磁盤存放路徑。檔案權限和其他屬性。但是,有名管道和普通檔案又有差別,有名管道并沒有在磁盤正存放真正的資訊,它存儲的通信資訊在記憶體中,兩程序結束後自動丢失,擁有一個磁盤路徑僅僅是一個接口,其目的是使程序間資訊的程式設計更簡單統一。通信的兩個程序結束後,有名管道的檔案路徑本身仍然存在,這是和無名管道不一樣的地方。有名管道的特性可以總結為一下幾點:
1.可以用于同一台PC下任意兩程序通信;
2.無法seek,要保持先入先出(FIFO)原則;
3.有名管道通信是單項的;
4.有名管道本身不會随通信雙方的結束而消失,但有名管道中的内容會随着通信雙方結束消失。
二、建立有名管道
mkfifo()
函數用來建立有名管道,其函數聲明如下:
// come from /usr/include/sys/stat.h
/*Create a new FIFO named PATH, with permission bits MODE*/
int mkfifo(const char* path, __mode_t mode);
第一個參數path為要建立的管道檔案名;
第二個參數mode為生成檔案的模式。
mkfifo()
會根據參數建立特殊的有名管道檔案,該檔案必須不存在。
mkfifo()
建立的FIFO檔案其他程序都可以用讀寫一般檔案的方式存取。當使用open()函數打開FIFO檔案是,O_NONBLOCK會有影響。
三、讀寫有名管道
有名管道的實質和無名管道一樣是一段核心管理的記憶體空間。但在通過write和read系統調用來執行讀寫操作前,需要調用open()函數來打開該檔案。另外,操作無名無名管道的阻塞位置為open()位置,而不是有名管道的讀寫位置。
1.打開有名管道
如果希望以寫的方式打開管道,則需要另一個程序以讀的方式打開管道,否則一直阻塞。反過來如果以讀的方式打開管道,則需要另一程序以寫的方式打開管道,否則一直阻塞。即,如果以某種方式打開有名管道,則系統将阻塞程序,直到有另一個程序(包括自己)以另一種方式打開該管道後才會繼續執行。顯然,一個程序以可讀可寫方式打開管道,目前程序充當了讀和寫兩個身份,程序不會阻塞。
2.兩程序已經完成打開管道操作,阻塞讀操作按以下方式執行:
a.如果管道中沒有資料,讀操作預設阻塞;
b.如果管道中有資料,但小于期望讀取資料量,讀出所有資料傳回;
c.如果管道中有資料,但大于期望讀取資料量,讀出期望大小資料傳回。
3.兩程序已經完成打開管道操作,阻塞寫操作按以下方式執行:
a.如果管道中沒有空間,寫操作阻塞;
b.如果管道中有空間,但小于期望寫入資料量,寫滿空間後阻塞;
c.如果管道中有空間,但大于期望寫入資料量,寫入資料後傳回。
4.兩程序已經完成打開管道操作,中途其中一個程序退出:
a.未退出一端如果是寫操作,将傳回SIGPIPE信号;
b.未退出一端如果是阻塞讀操作,讀操作将不再阻塞,直接傳回0。
四、示例
下面給出在父子程序間使用FIFO通信的示例,在非情緣關系的程序間原理是一樣的:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <fcntl.h>
#define FIFO_PATH "./fifo"
void handler(int sig)
{
printf("sig=%d\n", sig);
}
int main()
{
signal(SIGPIPE, handler);
unlink(FIFO_PATH); // 删除原來的管道
mkfifo(FIFO_PATH, ); // 建立管道
pid_t pid = fork();
if (pid < ) {
perror("fork error");
return -;
}
if ( == pid) { // 子程序
int fd;
fd = open(FIFO_PATH, O_WRONLY);
write(fd, "hello world!", );
sleep();
close(fd);
} else { // 父程序
int fd;
char buf[] = {0};
fd = open(FIFO_PATH, O_RDONLY);
int ret = read(fd, buf, sizeof(buf));
printf("ret=%d buf=%s\n", ret, buf);
close(fd);
}
return ;
}
運作結果如下所示: