命名管道出现的背景:
命名管道(FIFO文件/name pipe),之前提到的管道,虽然能够实现进程间的通信,但是局限性也很明显。首先,这个管道只能在具有血缘关系的进程之间通信;第二,他只能实现一个进程写,一个进程读,而如果需要两者同时进行时,就得重新打开一个管道。
为了使任意两个进程之间能够通信,就提出了命名管道
命名管道的特点:
1、与管道的区别:提供了一个路径名与之关联,以FIFO文件的形式存储于文件系统中,能够实现任何两个进程之间通信。而匿名管道对于文件系统是不可见的,它仅限于在父子进程之间的通信。
2、FIFO是一个设备文件,在文件系统中以文件名的形式存在,因此即使进程与创建FIFO的进程不存在血缘关系也依然可以通信,前提是可以访问该路径。
3、FIFO(first input first output)总是遵循先进先出的原则,即第一个进来的数据会第一个被读走。
创建管道的函数原型:
#include
int mknod(const char* path, mode_t mod, dev_t dev);
int mkfifo(const char* path, mode_t mod);
注释:这两个函数都能创建一个FIFO文件,该文件是真实存在于文件系统中的。函数 mknod 中参数 path 为创建命名管道的全路径; mod 为创建命名管道的模式,指的是其存取权限; dev为设备值,改值取决于文件创建的种类,它只在创建设备文件是才会用到。
返回值:这两个函数都是成功返回 0 ,失败返回 -1
mknod是比较老的函数,而使用mkfifo函数更加简单和规范,所以建议在可能的情况下,尽量使用mkfifo而不是mknod。
FIFO文件由mkfifo函数创建,它已经隐含指定了O_CREAT|O_EXCL。也就是说,如果FIFO存在就返回一个EEXIST错误,如果不存在就创建一个新的FIFO。FIFO由open打开而不是mkfifo。因此要打开一个已存在的FIFO或创建一个新的FIFO,应该先调用mkfifo,再检查它是否返回EEXIST错误,若是返回该错误,则改为调用open。
访问命名管道:
mkfifo函数只是创建一个FIFO文件,要使用命名管道还是将其打开。同打开其他文件一样,FIFO文件也可以使用open调用来打开,但有一点需要注意,就是传递给open调用的是FIFO的路径名,而不是正常的文件。
打开FIFO文件的方式有4种:
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
在open函数的调用的第二个参数中,选项 O_NONBLOCK 表示非阻塞,加上这个选项后,表示open调用是非阻塞的,如果没有这个选项,则表示open调用是阻塞的。
对于以只读方式(O_RDONLY)打开的FIFO文件,如果open调用是阻塞的(即第二个参数为O_RDONLY),除非有一个进程以写方式打开同一个FIFO,否则它不会返回;如果open调用是非阻塞的的(即第二个参数为O_RDONLY | O_NONBLOCK),则即使没有其他进程以写方式打开同一个FIFO文件,open调用将成功并立即返回。
对于以只写方式(O_WRONLY)打开的FIFO文件,如果open调用是阻塞的(即第二个参数为O_WRONLY),open调用将被阻塞,直到有一个进程以只读方式打开同一个FIFO文件为止;如果open调用是非阻塞的(即第二个参数为O_WRONLY | O_NONBLOCK),open总会立即返回,但如果没有其他进程以只读方式打开同一个FIFO文件,open调用将返回-1,并且FIFO也不会被打开。
对于以读写方式(O_O_RDWR)打卡的FIFO文件,有风险,若是写端以读写方式打开,由于有读有写,则open将不阻塞;然后若是只写不读,可能回导致缓存区慢;若是写端自己写自己读,则会导致其他进程读不到数据;若是读端以读写方式打开,open不阻塞;若是此时直接去读数据,则将会一直阻塞,等待其他进程往FIFO文件中写,若是其他进程不写,则会一直阻塞至死。
读写端1:N的情况:FIFO内部有自己的机制保证,多个客户端同时往一个FIFO文件中写入,能自己保证数据不错乱,读端则等待所有写端都写完后才会去读,故能读到所有写端写入的数据,但是原理不清楚,以实测
读写端N:1的情况:有且只能有一个读端读到数据,至于哪个不确定
写端能无限写,反正试过一直写,写入200M的数据,还能再写,读端会一直等等到写端写完,或者写端关闭,或者写端进程退出
例子:
fifowrite.c
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
const char *fifo_name = "/tmp/my_fifo";
int pipe_fd = -1;
int data_fd = -1;
int res = 0;
const int open_mode = O_WRONLY;
int bytes_sent = 0;
char buffer[PIPE_BUF + 1];
if (access(fifo_name, F_OK) == -1)
{
// 管道文件不存在
// 创建命名管道
res = mkfifo(fifo_name, 0777);
if (res != 0)
{
fprintf(stderr, "Could not create fifo %s\n", fifo_name);
exit(EXIT_FAILURE);
}
}
printf("Process %d opening FIFO O_WRONLY\n", getpid());
// 以只写阻塞方式打开FIFO文件,以只读方式打开数据文件
pipe_fd = open(fifo_name, open_mode);
data_fd = open("Data.txt", O_RDONLY);
printf("Process %d result %d\n", getpid(), pipe_fd);
if (pipe_fd != -1)
{
int bytes_read = 0;
// 向数据文件读取数据
bytes_read = read(data_fd, buffer, PIPE_BUF);
buffer[bytes_read] = '\0';
while (bytes_read > 0)
{
// 向FIFO文件写数据
res = write(pipe_fd, buffer, bytes_read);
if (res == -1)
{
fprintf(stderr, "Write error on pipe\n");
exit(EXIT_FAILURE);
}
// 累加写的字节数,并继续读取数据
bytes_sent += res;
bytes_read = read(data_fd, buffer, PIPE_BUF);
buffer[bytes_read] = '\0';
}
close(pipe_fd);
close(data_fd);
}
else
{
exit(EXIT_FAILURE);
}
printf("Process %d finished\n", getpid());
exit(EXIT_SUCCESS);
}
fiforead.c
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
const char *fifo_name = "/tmp/my_fifo";
int pipe_fd = -1;
int data_fd = -1;
int res = 0;
int open_mode = O_RDONLY;
char buffer[PIPE_BUF + 1];
int bytes_read = 0;
int bytes_write = 0;
// 清空缓冲数组
memset(buffer, '\0', sizeof(buffer));
printf("Process %d opening FIFO O_RDONLY\n", getpid());
// 以只读阻塞方式打开管道文件,注意与fifowrite.c文件中的FIFO同名
pipe_fd = open(fifo_name, open_mode);
// 以只写方式创建保存数据的文件
data_fd = open("DataFormFIFO.txt", O_WRONLY | O_CREAT, 0644);
printf("Process %d result %d\n", getpid(), pipe_fd);
if (pipe_fd != -1)
{
do
{
// 读取FIFO中的数据,并把它保存在文件DataFormFIFO.txt文件中
res = read(pipe_fd, buffer, PIPE_BUF);
bytes_write = write(data_fd, buffer, res);
bytes_read += res;
}
while (res > 0);
close(pipe_fd);
close(data_fd);
}
else
{
exit(EXIT_FAILURE);
}
printf("Process %d finished, %d bytes read\n", getpid(), bytes_read);
exit(EXIT_SUCCESS);
}
以上知识点均摘抄自网络,如若侵权,请私信联系删除