博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
IPC——管道和 FIFO
阅读量:4147 次
发布时间:2019-05-25

本文共 3546 字,大约阅读时间需要 11 分钟。

目录

匿名管道 pipe

管道由创建,提供一个单路(单向)数据流

int pipe(int pipefd[2]); // int pipe2(int pipefd[2], int flags); // O_NONBLOCK、O_ASYNC, 设置O_ASYNC标志会导致新输入变为时生成的信号(默认为SIGIO)    pipefd 为 pipe() 分配的两个文件描述符。    pipefd[0]:读文件描述符,内核对于管道的fd[0]描述符是以只读方式打开的。    pipefd[1]:写文件描述符,同理fd[1]是以只写方式打开的。

管道随进程:

在这里插入图片描述

单个进程内的管道,刚fork()之后:
在这里插入图片描述
父进程关闭读端,子进程关闭写端后:
在这里插入图片描述
应用:shell 命令
在这里插入图片描述
双向通道:
在这里插入图片描述
在这里插入图片描述
fd[1]和fd[0]都是可读写的,但是写入fd[1]的数据只能从fd[0]读取,反之亦然。数据流(数据通道)独立,不然很可能读取到自己写入的数据。

管道如何实现进程间的通信

  1. 父进程创建管道(在内核空间中创建了两个文件描述符,一个对应读,一个对应写,两个文件描述符对应的文件表项中指向的V节点存放的是管道对象的指针)
  2. 父进程fork出子进程,⼦进程也有两个⽂件描述符指向同⼀管道。
  3. 父进程关闭fd[0],子进程关闭fd[1],即⽗进程关闭管道读端,⼦进程关闭管道写端(因为管道只支持单向通信)。⽗进程可以往管道⾥写,⼦进程可以从管道⾥读,管道是⽤环形队列实现的,数据从写端流⼊从读端流出,这样就实现了进程间通信。

管道特点

  • 管道只允许单向通信
  • 管道内部保证同步机制,从而保证访问数据的一致性。
  • 面向字节流
  • 管道随进程,依赖于文件系统,它的生命周期随进程的结束结束,进程消失管道对应的端口也关闭,两个进程都消失管道也消失(一端关闭只影响一端的操作,另一端可以继续操作)。
  • 管道通常用于具有共同祖先的进程间通信,如父子进程间的通信。

popen 和 pclose 函数

作为关于管道的一个实例,就是标准I/O函数库提供的popen(),该函数创建一个管道,并fork一个子进程,该子进程根据popen()传入的参数,关闭管道的对应端,然后执行传入的shell命令,然后等待终止。

调用进程和fork的子进程之间形成一个管道。调用进程和执行shell命令的子进程之间的管道通信是通过popen返回的FILE*来间接的实现的,调用进程通过标准文件I/O来写入或读取管道。

pclose()会关闭由popen创建的标准I/O流,等待其中的命令终止,然后返回shell的执行状态。

char *cmd = "ls /usr/local/bin ";     FILE *p = popen(cmd, "r");    char buf[256];     while (fgets(buf, sizeof(buf), p) != NULL)    {        printf("%s\n", buf);    }       pclose(p);

FIFO(named pipe )

创建命名管道(两种方法):

  1. Shell下用命令 mknod 或 mkfifo 创建命名管道:mknod namedpipe
  2. 系统函数创建:mknod、
int mkfifo(const char *pathname, mode_t mode); open()
[root@idcserver program]# mkfifo skywalker[root@idcserver program]# echo "I have liked yuki..." >skywalker &[root@idcserver program]# cat < skywalkerI have liked yuki...[1]+  Done                    echo "I have liked yuki..." > skywalker

在这里插入图片描述

pipe
在这里插入图片描述
FIFO

FIFO 特点

  1. FIFO是一个设备文件,在文件系统中以文件名的形式存在,因此即使进程与创建FIFO的进程不存在血缘关系也依然可以通信,前提是可以访问该路径。命名管道是设备文件,它是存储在硬盘上的,而管道是存在内存中的特殊文件。
  2. FIFO(first input first output)总是遵循先进先出的原则,即第一个进来的数据会第一个被读走。
  3. 和管道一样,FIFO仅提供半双工的数据通信,即只支持单向的数据流。
  4. 与管道的区别:提供了一个路径名与之关联,以FIFO文件的形式存储于文件系统中,能够实现任何两个进程之间通信。而匿名管道对于文件系统是不可见的。
  5. 内核为每个 FIFO 维护一个管道对象,FIFO 必须在两端(读取和写入)都打开数据才能被通过。
  6. 命名管道调用open()打开有可能会阻塞,但是如果以读写方式(O_RDWR)打开则一定不会阻塞;以只读(O_RDONLY)方式打开时,调用open()的函数会被阻塞直到有数据可读(直到对方也打开);如果以只写方式(O_WRONLY)打开时同样也会被阻塞,原理同上。一端关闭或退出,另一端也退出。
  7. write 不阻塞,直到写满管道。read阻塞,直到有数据可读。
  8. FIFO 特殊文件在文件系统上没有内容。文件系入口仅作为参考点,以便进程可以使用文件系统中的名称访问管道。

  • 管道和 FIFO 之间的唯一区别是它们被创建和打开。一旦完成了这些任务完成后,管道和 FIFO 上的 I/O 完全相同语义
  • 通过启用O_NONBLOCK打开文件状态标志,可以实现非阻塞 I/O 。

阻塞的情况下:

  • 如果write的字节数小于等于PIPE_BUF,那么write会阻塞到写入所有数据,并且写入操作是原子的。
  • 如果write的字节数大于PIPE_BUF,那么write会阻塞到写入所有数据,但写入操作不是原子的,即write会根据当前缓冲区剩余的大小,写入相应的字节数,然后等待下一次有空余的缓冲区,这中间可能会有其他进程进行write操作。

非阻塞的情况下:

  • 如果write的字节数小于等于PIPE_BUF,且管道或FIFO有足以存放要写入数据大小的空间,那么就写入所有数据;
  • 如果write的字节数小于等于PIPE_BUF,且管道或FIFO没有足够存放要写入数据大小的空间,那么就会立即返回EAGAIN错误。
  • 如果write的字节数大于PIPE_BUF,且管道或FIFO有至少1B的空间,那么就内核就会写入相应的字节数,然后返回已写入的字节数;
  • 如果write的字节数大于PIPE_BUF,且管道或FIFO无任何的空间,那么就会立即返回EAGAIN错误。

管道和FIFO的限制

系统内核对于管道和FIFO的唯一限制为:OPEN_MAXPIPE_BUF;

OPEN_MAX:一个进程在任意时刻可以打开的最大描述符数。

PIPE_BUF: 标识一个管道可以原子写入管道和FIFO的最大字节数,并不是管道或FIFO的容量

Q&A

  1. 父子进程无名管道采用单工方式进行通信,只能一个写另外一个读。如果要实现双工,那么就创建两个无名管道来实现双工通信。

  2. 当无名管道相关联的所有文件描述符被关闭,那么无名管道对象会从内核中释放掉。

  3. Q: 匿名管道可否用于不同进程?不同线程?如何用?A: 无名管道不单可以在父子进程中,也可用于不同的程序中。要实现A程序通过无名管道与B程序进行通信,那么可以在A程序中采用fork+exec模式启动B程序,同样可以把文件描述符复制到B程序中,这样就可以使用管道文件描述符进行通信了。

  4. 管道与FIFO的区别: 在于无名管道没有具体的文件,而有名管道在文件系统中有对应的文件(但是FIFO依然是随进程的,进程消失,管道内的内容失效)。

  5. 管道读取数据的四种的情况(O_NONBLOCK默认未设置):

    (1)读端不读,写端一直写——写端不阻塞,直到写满管道才阻塞
    (2)写端不写,但是读端一直读——读端阻塞
    (3)读端一直读,且fd[0]保持打开,而写端写了一部分数据不写了,并且关闭fd[1]。——通道关闭的条件是所有的fd都关闭,所以此时通道未关闭,可以将通道内的消息继续读完。
    (4)读端读了一部分数据,不读了且关闭fd[0],写端一直在写且f[1]还保持打开状态。——原理同上。可以继续写,直到通道写满。

  6. 管道容量大小 : /proc/sys/fs/pipe-max-size(以字节为单位)(1048576=1 MiB)

  7. PIPE_BUF :大于PIPE_BUF字节的写入(见pipe(7))将被分割成多个数据包。

转载地址:http://oviti.baihongyu.com/

你可能感兴趣的文章
【Python基础6】格式化字符串
查看>>
【Python基础7】字典
查看>>
【Python基础8】函数参数
查看>>
【Python基础9】浅谈深浅拷贝及变量赋值
查看>>
Jenkins定制一个具有筛选功能的列表视图
查看>>
【Python基础10】探索模块
查看>>
【Python】将txt文件转换为html
查看>>
[Linux]Shell脚本实现按照模块信息拆分文件内容
查看>>
idea添加gradle模块报错The project is already registered
查看>>
在C++中如何实现模板函数的外部调用
查看>>
在C++中,关键字explicit有什么作用
查看>>
C++中异常的处理方法以及使用了哪些关键字
查看>>
如何定义和实现一个类的成员函数为回调函数
查看>>
内存分配的形式有哪些? C++
查看>>
什么是内存泄露,如何避免内存泄露 C++
查看>>
栈和堆的空间大小 C++
查看>>
什么是缓冲区溢出 C++
查看>>
sizeof C++
查看>>
使用指针有哪些好处? C++
查看>>
引用还是指针?
查看>>