Loading

快乐Linux —— 12. 管道和FIFO

参考:

https://blog.csdn.net/rengui1228/article/details/72977797

简述

最初,我们 IPC 只有管道,它没有名字,只能由具有亲缘关系的进程使用。后来 FIFO 加入,FIFO 又称为有名管道,可以由同一主机任意多个进程使用。后文为方便起见,分别以无名管道和有名管道称呼它们。

无名管道

特点:

  • 只能应用于具有亲缘关系的两个进程间。
  • 它是一个半双工的通信模式,具有固定的读端和写端。
  • 可以看成一种特殊的文件,对于它的读写也可以使用普通的read()和write()等函数。但是她不是普通的文件,并不属于其它任何文件系统,并且只存在于内核的内存空间中。
#include<unistd.h>
int fd[2];
int pipe(int fd[2]);
//创建并打开管道

pipe 若失败返回-1,成功的话在内存上创建一个管道,令 fd[0] fd[1] 都指向这个管道起点,返回0。

左侧为实际模型,右侧为逻辑模型。

单进程下的管道很少使用,一般情况下往往是父子进程利用管道交互数据。当执行完pipe后创建子进程。

若需要从父到子传递数据,则关闭父的读端,子的写端。形成以下逻辑关系。

可以看到数据流是单向从父进程流向子进程。

如果想要从子进程到父进程传递数据则需要再创建一条管道,两条管道,分别单向传递数据。

接下来就可以使用系统调用IO函数通过fd读写这个管道。

#include <stdio.h>
#include <assert.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

int main()
{
	int fd[2];
	assert(pipe(fd)==0);

	pid_t pid;
	assert((pid = fork())!=-1);

	if(pid==0)
	{
		while(1)
		{
			close(fd[1]);
			char buff[128];
			read(fd[0],buff,128);
			if(!strncmp(buff,"exit",4))
			{
				puts("child exit");
				close(fd[0]);
				exit(0);
			}
			printf("child  write:  ");
			printf(buff);
		}
	}
	else
	{
		while(1)
		{
			close(fd[0]);
			char buff[128];
			printf("parent read:  ");
			fgets(buff,128,stdin);
			write(fd[1],buff,128);
			if(!strncmp(buff,"exit",4))
			{
				puts("parent exit");
				close(fd[1]);
				exit(0);
			}
		}
	}
	return 0;
}

无名管道将会在所有进程关闭它后自动销毁。

对一个管道文件进行lseek 会出错,返回-1,并且设置errno 为 ESPIPE。

有名管道

特点:

  • 能应用于同一个主机上的任意两个进程间。
  • 可以看作特殊的文件,只在磁盘上占文件标识,在磁盘上放置一个管道号,即inode节点,而不占用block,所谓的block放置在内存上。

创建方式

  • bash命令创建 mkfifo
  • 系统调用 int mkfifo(const char *name , int mode);

管道是在内存上一个大小固定的缓冲区。在Linux 上一般大小为1页,即4k。

当管道中数据不足 read 的大小,则只返回已有数据。在写管道时可能会写满,当这种情况发生时,随后对管道的write()调用将默认地被阻塞,等待某些数据被读取,以便腾出足够的空间供write()调用写。

当一个进程写,多个进程读时,谁读走数据不确定,不过数据一旦被读走,所有读进程的偏移量都会改变

像操作文件一样, 在写时可以把偏移量向前置,读出写的内容,但在读的时候不能在读后再把偏移量前置。

总结

两者的区别:

  1. 创建并打开一个无名管道只需要pipe,而创建并打开一个有名管道需要在调用mkfifo后调用open。
  2. 无名管道在所有与它关联的进程结束后自动有操作系统回收,而有名管道需要用unlink函数主动删除。

无名管道因为没有名字,只能通过创建时传入pipe的fd数组实参进行访问,而这个数组在fork生成子进程时顺便复制到了子进程中,所以只有亲缘关系的进程间才能访问同一个管道。

而FIFO 在文件系统上有一个文件路径名,不同进程可以通过open打开同一个FIFO,从而进行通讯。

关于管道文件读写阻塞的问题:

其中的 O_NONBLOCK 这样使用 open(“myfifo”, O_WRONLY | O_NONBLOCK);

posted @ 2020-02-13 14:23  沉云  阅读(170)  评论(0编辑  收藏  举报