一 概述
命名管道(FIFO) 跟管道(pipe)最大的一个区别,要让无亲缘关系的两个进程间通讯,管道是做不到的。而命名管道其实是跟路径名关联的,从而允许无亲缘关系的两个进程间通讯。
二 管道函数的使用
mkfifo函数
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *filename,mode_t mode);
返回:若成功则为 0 ,若出错则为 -1
FIFO其实是一种文件类型,判断是否是FIFO类型文件,可用S_ISFIFO。
而mkfifo 可以创建该文件类型的文件。mkfifo 创建文件时隐含指定O_CREAT|O_EXCL,(不存在会创建,存在返回EEXIST错误)。因此创建文件可以用mkfifo,打开已经创建的文件则可以用open。(一般情况下先调用mkfifo,检测错误是否是EEXIST,是的话调用open即可)
注意点:以下几条特性,不管是pipe管道,还是命名管道(FIFO)都适用,下面只写了管道,其实包括了命名管道,pipe管道这两种管道。
正文,详细说明往管道write 情况
- 写管道时,常熟PIPE_BUF规定了内核中管道的缓存区大小,write应要求写的字节数小于等于PIPE_BUF。这时写操作不会与其他进程的写操作穿插进行。write操作保证是原子的,一旦有个进程的写的字节大于PIPE_BUF,数据就可能会穿插进行。
- 引入几个变量
1.待写入的字节数 nwriteSize
2.write返回实际写入的字节数 nreturnSize
3.PIPE_BUF 内核中管道的缓存区大小
4.管道中当前可用的空间大小 Left_PIPE_BUF 。
当非阻塞模式下O_NONBLOCK ,写管道时。
(1) nwriteSize < Left_PIPE_BUF 全部写入管道
(2) Left_PIPE_BUF < nwriteSize < PIPE_BUF 返回EAGAIN错误。因为nwriteSize < PIPE_BUF 需要保持原子性,确保一次性写的数据是完整的。而 Left_PIPE_BUF < nwriteSize 使得数据不能全部一次性写入。阻塞情况下会等待Left_PIPE_BUF >nwriteSize 时写入,但是非阻塞模式,就会返回错误
(3) nwriteSize > PIPE_BUF Left_PIPE_BUF有多少空间就会写入多少字节,若Left_PIPE_BUF = 0 ,返回EAGAIN 错误 - 对于往管道write的一端并没有打开读的,返回SIGPIPE信号,要是不抓获或者不设置忽略该信号,默认终止该进程。若设置了忽略则返回EPIPE错误。
三 无亲缘关系FIFO使用
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#define FIFO1 "/tmp/fifo1"
#define FIFO2 "/tmp/fifo2"
int ipc_fifo(){
int readfd, writefd;
pid_t childpid;
if(mkfifo(FIFO1,0) <0 && (errno != EEXIST)){
return -1;
}
if(mkfifo(FIFO2,0) <0 && (errno != EEXIST)){
return -1;
}
childpid = fork();
if(childpid ==0){
readfd= open(FIFO1,O_RDONLY,0);
writefd = open(FIFO2,O_WRONLY,0);
char buffer[128] = {0};
read(readfd,buffer,128);
printf("child read buffer:%s\n",buffer);
write(writefd,"child tell parent something\n",strlen("child tell parent something\n"));
close(readfd);
close(writefd);
exit(0);
}
writefd = open(FIFO1,O_WRONLY,0);
readfd = open(FIFO2,O_RDONLY,0);
write(writefd,"parent tell child something\n",strlen("parent tell child something\n"));
char buffer[128] = {0};
read(readfd,buffer,128);
printf("parent read buffer:%s\n",buffer);
waitpid(childpid,NULL,0);
close(readfd);
close(writefd);
unlink(FIFO1);
unlink(FIFO2);
return 1;
}
运行后结果如下:
四 总结
通过上面例子,你可以发现跟之前的pipe管道有两个不同的点
- 创建打开管道只需调用一次pipe,而创建打开fifo则需要调用mkfifo后再调用open。
- 管道程序退出后也就自动消失了,而fifo的名字则需要调用unlink才从文件系统中删除。即上面的程序如果不调用unlink。你可以在tmp下找到 fifo1 ,fifo2.
- 同时fifo跟管道比是可以在无亲缘关系的两个进程间通讯的,而上面例子没有单独写两个无关缘进程。有兴趣可以自己实现下