Bootstrap

linux 进程间通信-FIFO(有名管道)

FIFO

FIFO常被称为命名管道,以区分管道(pipe)。管道(pipe)只能用于“有血缘关系”的进程间。但通过FIFO,不相关的进程也能交换数据。

FIFO是Linux基础文件类型中的一种。但,FIFO文件在磁盘上没有数据块,仅仅用来标识内核中一条通道。各进程可以打开这个文件进行read/write,实际上是在读写内核通道,这样就实现了进程间通信。

有名管道突破了这种限制,可以实现互不相关的进程实现彼此的通信,管道可以通过路径名指定,在系统是可见的,建立管道之后就可以想普通文件一样进行读写,FIFO严格遵守先进先出的原则,读总是从开始读取数据,写数据写入末尾,不支持lseek()文件定位操作
在这里插入图片描述

使用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写入数据,但是对方没打开读端就会出现这样的错误。

;