10 管道
数据可以从一个进程流向另一个进程
10.1 匿名管道PIPE
匿名管道有以下的特征:
-
没有名字,因此不能使用open()函数打开,但可以使用close()函数关闭。
-
只提供单向通信(半双工),也就是说,两个进程都能访问这个文件,假设进程1往文件内写东西, 那么进程2就只能读取文件的内容。
-
只能用于具有血缘关系的进程间通信,通常用于父子进程建通信 。
-
管道是基于字节流来通信的。
-
依赖于文件系统,它的生命周期随进程的结束而结束。
-
写入操作不具有原子性,因此只能用于一对一的简单通信情形。
-
管道也可以看成是一种特殊的文件,对于它的读写也可以使用普通的read()和write()等函数。 但是它又不是普通的文件,并不属于其他任何文件系统,并且只存在于内核的内存空间中, 因此不能使用lseek()来定位。
10.2 命名管道FIFO
命名管道有以下的特征:
-
有名字,存储于普通文件系统之中。
-
任何具有相应权限的进程都可以使用 open()来获取命名管道的文件描述符。
-
跟普通文件一样:使用统一的 read()/write()来读写。
-
跟普通文件不同:不能使用 lseek()来定位,原因是数据存储于内存中。
-
具有写入原子性,支持多写者同时进行写操作而数据不会互相践踏。
-
遵循先进先出(First In First Out)原则,最先被写入 FIFO的数据,最先被读出来。
10.3 pipe()函数
int pipe(int pipefd[2]);
数组pipefd是用于返回两个引用管道末端的文件描述符, pipefd[0] 指管道的读取端,pipefd[1]指向管道的写端 , 向管道的写入端写入数据将会由内核缓冲,即写入内存中,直到从管道的读取端读取数据为止, 而且数据遵循先进先出原则。pipe()函数还会返回一个int类型的变量, 如果为0则表示创建匿名管道成功,如果为-1则表示创建失败,并且设置errno。
匿名管道创建成功以后,创建该匿名管道的进程(父进程)同时掌握着管道的读取端和写入端, 但是想要父子进程间有数据交互,则需要以下操作:
-
父进程调用pipe()函数创建匿名管道,得到两个文件描述符pipefd[0]、pipefd[1], 分别指向管道的读取端和写入端。
-
父进程调用fork()函数启动(创建)一个子进程, 那么子进程将从父进程中继承这两个文件描述符pipefd[0]、pipefd[1], 它们指向同一匿名管道的读取端与写入端。
-
由于匿名管道是利用环形队列实现的,数据将从写入端流入管道,从读取端流出,这样子就实现了进程间通信, 但是这个匿名管道此时有两个读取端与两个写入端。
-
如果想要从父进程将数据传递给子进程,则父进程需要关闭读取端,子进程关闭写入端
-
如果想要从子进程将数据传递给父进程,则父进程需要关闭写入端,子进程关闭读取端
-
当不需要管道的时候,就在进程中将未关闭的一端关闭即可
10.4 fifo()函数
int mkfifo(const char * pathname,mode_t mode);
mkfifo()会根据参数pathname建立特殊的FIFO文件,而参数mode为该文件的模式与权限。
mkfifo()创建的FIFO文件其他进程都可以进行读写操作,可以使用读写一般文件的方式操作它, 如open,read,write,close等。
mode模式及权限参数说明:
-
O_RDONLY:读管道。
-
O_WRONLY:写管道。
-
O_RDWR:读写管道。
-
O_NONBLOCK:非阻塞。
-
O_CREAT:如果该文件不存在,那么就创建一个新的文件,用第三个参数为其设置权限
-
O_EXCL:如果使用O_CREAT时文件存在,那么可返回错误消息。可测试文件是否存在。
函数返回值说明如下:
-
0:成功
-
EACCESS:参数 filename 所指定的目录路径无可执行的权限。
-
EEXIST:参数 filename 所指定的文件已存在。
-
ENAMETOOLONG:参数 filename 的路径名称太长。
-
ENOENT:参数 filename 包含的目录不存在。
-
ENOSPC:文件系统的剩余空间不足。
-
ENOTDIR:参数 filename 路径中的目录存在但却非真正的目录。
-
EROFS:参数 filename 指定的文件存在于只读文件系统内。
使用FIFO的过程中,当一个进程对管道进行读操作时:
-
若该管道是阻塞类型,且当前FIFO内没有数据,则对读进程将一直阻塞到有数据写入。
-
若该管道是非阻塞类型,不论FIFO内是否有数据,读进程都会立即执行读操作。 即如果FIFO内没有数据,读函数将立刻返回 0。
使用FIFO的过程中,当一个进程对管道进行写操作时:
-
若该管道是阻塞类型,则写操作将一直阻塞到数据可以被写入。
-
若该管道是非阻塞类型而不能写入全部数据,则写操作进行部分写入或者调用失败