顾名思义,管道就像是将数据放入到一个长长的管子中一样,肯定会有一端写入数据,称为写端,有一段读出数据,称为读端。既然是说像管子一样那么它肯定有大小吧,资源不是无穷无尽的,默认下管道的大小是64k,用ulimit -a 可以查看。
1、无名管道:
- 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立两个管道
- 无名管道只能用于父子进程之间(因为父子进程共享打开的文件描述符)
- 无名管道对于管道两端的进程而言就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自己单独构成一种文件系统,并且只存在在内核中。(这也是他无法在没有亲缘关系的进程中使用的原因)
- 数据的读出和写入:一个进程向管道的一端写数据,另一个进程在管道的另一端读。
无名管道的创建:
Int pipe(int pipefd[2]);当一个管道创建时,它会产生两个文件描述符fd[0]和fd[1],分别为读端和写端。现在假设fork()创建一个新进程,父进程负责向管道中写数据,子进程复制读数据。那么父进程要关闭读端,子进程要关闭写端。
规则:
- 写端不存在时,则认为已经读到数据的尾部,读函数返回读到的字节数为0
- 写端存在时,如果写的字节数大于PIPE_BUF(管道默认大小65535,64k)则返回管道现有数据,如果小于PIPE_BUF,则返回实际大小。
- 当写端存在,管道没有数据时,读取管道时会阻塞
- 当读端存在时,向管道中写数据才有意义。否则,管道写端进程会收到内核传来的SIGPIPE信号,用户程序可以处理该信号,也可以忽略(默认使应用程序终止)
2、有名管道
有名管道的文件形式存在于文件系统中,这样就可以是任意两个进程之间通信。FIFO文件严格遵循先进先出的规则,对文件的读总是从开始处返回数据,对文件的写则把数据加到末尾。都不支持lseek()等文件定位操作。
注意:有名管道的名字存在与文件系统中,内容存放在内存中。
有名管道的创建:
Int mkfifo(const char *pathname ,mode_t mode)
第一个参数:一个路径名,也就是创建后FIFO的名字
第二个参数:权限设置
如果mkfifo的第一个参数已存在时,返回一个EEXIST错误。
有名管道比无名管道多一个打开操作:open
打开规则:
(1)如果当前为读而打开文件,若有相应进程为写打开了文件,则当前打开操作成功返回,否则会阻塞到有进程为写打开文件。
(2)如果当前为写打开文件,若有相应进程为读打开了文件,则当前打开操作成功返回,否则会阻塞到有进程为打开文件。
3、有名管道和无名管道的区别
其实在上面介绍两者时已经把它们的区别说了最主要的一点是:
- 无名管道只能在父子进程直接通信,有名管道可以在任意两个进程间通信
- 无名管道是进程创建管道时存在的,它不存在于文件系统中,故别的进程无法看到它所以不能和它通信。有名管道存在于文件系统中,别的进程可以看到它的文件描述符,但是它们的内容都是存在内存中而非磁盘上。