linux 進程間通信-FIFO(有名管道)

FIFO

FIFO常被稱爲命名管道,以區分管道(pipe)。管道(pipe)只能用於“有血緣關係”的進程間。但通過FIFO,不相關的進程也能交換數據。
FIFO是Linux基礎文件類型中的一種。但,FIFO文件在磁盤上沒有數據塊,僅僅用來標識內核中一條通道。各進程可以打開這個文件進行read/write,實際上是在讀寫內核通道,這樣就實現了進程間通信。
在這裏插入圖片描述

使用mkfifo函數創建FIFO

mkfifo函數就是用於創建一個FIFO文件

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname,  mode_t mode);  

返回值說明:成功返回0; 失敗返回-1,設置errno

參數pathname:要創建的FIFO文件名且該文件必須不存在

參數mode:指定FIFO文件的權限(8進制數,如0664)

一旦使用mkfifo創建了一個FIFO,就可以使用open打開它,常見的文件I/O函數都可用於fifo。如:close、read、write、unlink等。

FIFO的打開規則

1)、如果當前打開操作是爲讀而打開FIFO時,若已經有相應進程爲寫而打開該FIFO,則當前打開操作將成功返回;否則,可能阻塞直到有相應進程爲寫而打開該FIFO(當前打開操作設置了阻塞標誌);或者,成功返回(當前打開操作沒有設置阻塞標誌)。
2)、如果當前打開操作是爲寫而打開FIFO時,如果已經有相應進程爲讀而打開該FIFO,則當前打開操作將成功返回;否則,可能阻塞直到有相應進程爲讀而打開該FIFO(當前打開操作設置了阻塞標誌);或者,返回ENXIO錯誤(當前打開操作沒有設置阻塞標誌)。

總之,一旦設置了阻塞標誌,調用mkfifo建立好之後,那麼管道的兩端讀寫必須分別打開,有任何一方未打開,則在調用open的時候就阻塞。對管道或者FIFO調用lseek,返回ESPIPE錯誤。
例子:
在當前目錄創建FIFO文件

mkfifo("./test", 0664);

fifo_w.c

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

int main() {
	int fd = 0;
	int len = 0;
	int ret;
	char buf[1024] = { 0 };


#if 1
	//只寫方式打開test
	fd = open("./test", O_WRONLY);
	if (fd < 0)
	{
		perror("open error");
		exit(1);
	}
#endif
#if 0
	fd = open("./test", O_WRONLY | O_NONBLOCK);//設置成非阻塞
	if (fd < 0)
	{
		//判斷是否爲對端沒打開而導致ENXIO錯誤
		if (errno == ENXIO)
		{
			perror("open error");
		}
		exit(1);
	}


#endif
	puts("open fifo write");
	//向FIFO寫入數據
	while ((len = read(STDIN_FILENO, buf, sizeof(buf))) > 0)
	{
		write(fd, buf, len);
	}
	close(fd);
	return 0;
}

fifo_r.c

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

int main() {
	int fd = 0;
	int len = 0;
	char buf[1024] = { 0 };

	//只讀打開test
	fd = open("./test", O_RDONLY);
	if (fd < 0)
	{
		perror("open error");
		exit(1);
	}
	puts("open fifo read");

	//從管道中讀取數據
	while ((len = read(fd, buf, sizeof(buf))) > 0)
	{
		write(STDOUT_FILENO, buf, len);
	}

	//如果read返回0,說明讀到文件末尾或對端已關閉
	if (len == 0)
	{
		puts("peer is close or file end");
	}
	else
	{
		puts("read error");
	}

	close(fd);
	return 0;
}


可以看到,先打開寫端時,由於此時讀端沒有打開,所以寫端會阻塞:
在這裏插入圖片描述
然後打開讀端,就可以進行通信了:
在這裏插入圖片描述
最後,當寫端按下Ctrl+d後關閉,對應的讀端的read就會返回0。
在這裏插入圖片描述

使用非阻塞IO

如果在打開FIFO文件不希望阻塞時,在調用open函數可以指定O_NONBLOCK。
即在fifo_w.c中使用這段代碼,然後我們先執行./fifo_w,此時並未執行./fifo_r

#if 1
	fd = open("test", O_WRONLY | O_NONBLOCK);//設置成非阻塞
	if (fd < 0)
	{
		//判斷是否爲對端沒打開而導致ENXIO錯誤
		if (errno == ENXIO)
		{
			perror("open error");
		}
		exit(1);
	}


#endif

在這裏插入圖片描述
出錯原因在於,當你打開FIFO寫入數據,但是對方沒打開讀端就會出現這樣的錯誤。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章