管道和FIFO的特征之一是它们的数据是一个字节流。这是UNIX的原生I/O模型。进程往其中写入的是字节流,系统不对它作解释。
FIFO不存数据,只是通过它找到内核文件。
一.建立有名管道
1.命令mknod :
mknod name p/s/m //创建管道文件/信号量/共享内存
2.命令mkfifo 创建管道
mkfifo -m 664 k2 //创建一个访问权限为664的管道文件k2
3作为函数:mkfifo
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
【注意】
mkfifo已隐含指定O_CREAT|O_EXCL。即它要么创建一个新的FIFO,要么返回一个EEXIST错误。如果只是希望打开而不创建文件,那就应调用open而不是mkfifo。
要打开一个已存在的FIFO或创建一个新的FIFO,应先调用mkfifo,检查它是否返回EEXIST错误,若返回则改为调用open。
另:open函数
open(const char *path, O_RDONLY);//1 open(const char *path, O_RDONLY | O_NONBLOCK);//2 open(const char *path, O_WRONLY);//3 open(const char *path, O_WRONLY | O_NONBLOCK);//4
1、就是程序不能以O_RDWR(读写)模式打开FIFO文件进行读写操作,而其行为也未明确定义,因为如一个管道以读/写方式打开,进程就会读回自己的输出,同时我们通常使用FIFO只是为了单向的数据传递。
2、就是传递给open的是FIFO的路径名,而不是文件名。
3.第二个参数中的选项O_NONBLOCK,选项O_NONBLOCK表示非阻塞,加上这个选项后,表示open调用是非阻塞的,如果没有这个选项,则表示open调用是阻塞的。
以只读方式和只写方式打开文件时要注意:
读端打开,如果写端已打开则打开操作成功返回,如果写端没打开,则读端阻塞。如果是非阻塞函数,则成功返回,(读到文件结束标志)
写端打开,如果读端已打开则打开操作成功返回,如果读端没打开,则写端阻塞。如果是非阻塞函数,则返回ENXIO错误(产生信号SIGPIPE)
从FIFO中读取数据:
1. 读端的阻塞标志只对本进程第一个读操作施加作用,如果本进程内有多个读操作序列,则在第一个读操作被唤醒并完成读操作后,其它将要执行的读操作将不再阻塞,即使在执行读操作时,FIFO中没有数据也一样(此时,读操作返回0)。
2. 对于设置了阻塞标志的读操作说,造成阻塞的原因有两种:当前FIFO内有数据,但有其它进程在读这些数据;另外就是FIFO内没有数据。解阻塞的原因则是FIFO中有新的数据写入,不论写入数据量的大小,也不论读操作请求多少数据量。就是说读操作同时只可以有一个进程进行。
3. 如果有进程写打开FIFO,且当前FIFO内没有数据,则对于设置了阻塞标志的读操作来说,将一直阻塞。对于没有设置阻塞标志读操作来说则返回-1,当前errno值为EAGAIN,提醒以后再试。
向FIFO中写入数据:
1.对于设置了阻塞标志的写操作:
- 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果此时管道空闲缓冲区不足以容纳要写入的字节数,则进入睡眠,直到当缓冲区中能够容纳要写入的字节数时,才开始进行一次性写操作。
- 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。FIFO缓冲区一有空闲区域,写进程就会试图向管道写入数据,写操作在写完所有请求写的数据后返回。(这时读端可能间歇性得到数据)
2.对于没有设置阻塞标志的写操作:
- 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果当前FIFO空闲缓冲区能够容纳请求写入的字节数,写完后成功返回;如果当前FIFO空闲缓冲区不能够容纳请求写入的字节数,则返回EAGAIN错误,提醒以后再写;
- 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。在写满所有FIFO空闲缓冲区后,写操作返回。
FIFO内核实现时可以支持双向通信。(pipe单向通信,因为父子进程共享同一个file 结构体)
先关闭只读方式打开的文件,再以只写方式打开文件。这样就可以实现双向通信了。但最好使用一对FIFO
验证代码:
先写后读:
#include <stdio.h> #include <stdlib.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #define MSG "hello" int main() { int fd; fd = open("./fio",O_WRONLY); printf("w:send msg:%s ",MSG); write(fd,MSG,sizeof(MSG)); sleep(5); close(fd); fd = open("./fio",O_RDONLY); char buf[1024]; memset(buf,0,sizeof(buf)); read(fd,buf,sizeof(buf)); printf("w,recv:%s ",buf); }
先读后写:
#include <stdio.h> #include <stdlib.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include <string.h> #include <fcntl.h> int main() { int fd; fd = open("./fio",O_RDONLY); char buf[1024]; memset(buf,0,sizeof(buf)); read(fd,buf,sizeof(buf)); printf("r:recv msg:%s ",buf); sleep(5); close(fd); fd = open("./fio",O_WRONLY); printf("r:send:%s ","fighting"); write(fd,"fighting",9); }
安全问题:
个FIFO文件,有多个进程同时向同一个FIFO文件写数据,而只有一个读FIFO进程在同一个FIFO文件中读取数据时,会发生数据块的相互交错。
根据上面的知识我们知道:
如果所有的写请求都是发往一个阻塞的FIFO的,并且每个写记请求的数据长度小于等于PIPE_BUF字节,系统就可以确保数据决不会交错在一起。
模型:
1-1
n-1
n-1-n
fgets,它读取以换行符“ ”结尾的字符串.
小结:
管道文件。它占用i节点块和数据块,在目录文件中记载了文件和i节点对应关系的管道是有名管道,没有记载的是无名管道。
管道文件一旦读出,就从管道中删除,所以它具有不可再现性。