管道
没有名字标识,决定了只能用于父子进程间的IPC
.半双工模式
int pipe(int fd[2]);
fd[0]
用于读取,fd[1]
用于写入.
上图为父子进程通过两个管道对象实现全双工通信的例子
FIFO
有名字标识,文件系统路径名,半双工模式.
// 默认包含O_CREAT | O_EXCL
int mkfifo(const char *pathname, mode_t mode);
使用模式:
(1). 用mkfifo
创建FIFO
对象,若返回EEXIST
,再调open
打开此已存在FIFO
对象
(2). mkfifo
创建成功后,依然用open
来打开使用
(3). fifo
是文件系统对象,删除需用unlink
fifo
和管道不支持seek
操作
非阻塞的指定
非阻塞的指定
(1). 对管道
调fcntl
传F_GETFL
,将返回结果添加O_NONBLOCK
,再调fcntl
传F_SETFL
,及更新后结果
(2). 对fifo
open
时参数2
指定O_NONBLOCK
或1
中方法均可
FIFO比较晦涩的方面
以下均基于阻塞调用前提
(1). 打开申请
对一个指定的FIFO
内核记录所有关联到此FIFO的进程访问请求.
对进程,执行对fifo
对象某权限的open
时,阻塞,条件满足才返回.
进程对fifo
的open
请求是否能满足取决于内核内核只在既收到对此fifo
读打开请求,又收到对此fifo
写打开请求时,才会对open
请求进行满足,允许其按指定权限打开并返回.
可归纳为:
a. 初始时,内核等待同时有对某fifo
对象的读打开请求和写打开请求时刻,才同时满足两个请求.再次之前,先到达的请求必须等待.多个不同进程发出的同类型请求均等待.直到某进程A
发出另一请求,这时,前面等待的所有请求,和进程A
的请求全部被满足,得以继续执行后续指令.
b. 后续,某请求请求以某权限打开此fifo
,若此fifo
已被以另一权限打开,则请求可被满足.否则,阻塞等待.
c. 对读打开fifo
执行read
,若FIFO
内缓冲区有可读数据,进行读取返回.读取的数据不会超过缓冲区容量,也不会超过可读数据数量.若FIFO
内缓冲区无数据可读,若,此时FIFO
没有被写打开,读取失败,产生SIGPIPE
信号.若,此时FIFO
有被写打开,读取阻塞,等待.
d. 对写打开fifo
执行写,若此FIFO
没有被读打开,写入失败,产生SIGPIPE
信号.若所写数据量小于PIPE_BUF
,若FIFO
可写空间可容纳写入数据,一次写入并返回.若FIFO
可写空间小于待写入数据,阻塞等待.若所写数据量大于PIPE_BUF
.若FIFO
已满,返回错误.若FIFO
未满,写入数据量取 要求写入的数据流和FIFO
可写空间较小者.
终端read/write
(1). 终端底层由内核来对其维护
终端接收键盘输入,将输入信息存储在终端进程的缓冲区.
每次键入换行时,会将一行输入连同换行字符,放入缓冲区.每次键入Ctrl+D
,产生一个EOF
特殊字符,放入缓冲区.
(2). 对终端执行read
时,终端依据read
要求的数据量,依次从终端输入缓冲区取出字符,最多取要求数量的字符后,read
返回.
特殊情况:
a. 取字符过程中若遇到\n
,则从输入缓冲区取出此\n
,且放入read
指定缓冲区,此字符计入read
获取的字符计数中.此后read
返回.
b. 取字符过程中若遇到EOF
(键入Ctrl+D
产生的EOF
是输入缓冲区一个字符),则EOF
从输入缓冲区取出此EOF
,但不放入read
指定缓冲区,此字符不计入read
获取的字符计数中.此后read
返回.
c. 对终端执行write
对普通字符,显示其字符内容.对换行符,显示一个换行效果
关于EOF
:
a. 对终端,EOF
也只是在其缓冲区中占据一个字节,EOF
的特殊作用如上所述.但一旦EOF
被取出生效,缓冲区EOF
后仍然可放入正常字符并进行后续处理.
b. 对文件
文件在当前文件位置距离文件结束位置不足要求的读取数据量时,一次读完剩余数据并返回.后续再次read
时,就是文件位置已经在结束位置然后继续读的情形,此时不论读多少次,每次都是以读0
字节返回.
c. 对套接字
在缓冲区可读数据余量小于要求量但已经收到FIN
时,一次读完余量返回.套接字只在收到对端的FIN
时,且缓冲区已经没有可读数据时,返回0
.此后,执行一次或多次read
,每次都是以读取0
字节返回.
字节流数据传输
(1). 管道/FIFO
提供的是字节流数据传输服务
多个客户同时写一个管道/FIFO时,一般单个写入小于PIPE_BUF
,对写入排队,顺序缓存写入信息.
(2). 从管道/FIFO读取信息
有时希望获取一个请求的边界.可采取方法:
a. 发送时以特殊字符作为一个请求终止符
接收时,先读取一块并缓存.在逐个扫描,扫描到终止符,即获得一个完整请求.
b. 每个请求按长度,内容组织.
读取时,先读取到长度,再按长度取完整请求内容.
c. 每次发起请求时,
均是建立连接,发起请求,断开连接. 一个请求一个连接,通过断开连接表示请求结束.