无名管道和有名管道
先说一下有名管道和无名管道用的函数:
无名管道使用的是 pipe()
有名管道使用的是fifo()
无名管道主要用于有血缘关系的两个进程间通信,是内核使用环形队列机制实现,借助内核缓冲区实现的。有名管道主要用于两个不相干的进程间通信,我认为之所以叫有名管道是因为他们借助mkfifo()函数创建的伪文件利用内核缓冲区进行通信,因为创建文件可以指定文件名所以操作和使用文件几乎一样。
首先关于无名管道 pipe()函数 需要指定两个文件描述符,通过pipe()函数创建一个管道使其一端读文件一端写文件。而fifo()函数是一个进程写数据,一个进程读数据,两个进程不能结束
1、无名管道
无名管道是作为一个半双工的通信模式,具有固定的读写端。并且他只能在具有亲缘关
系的进程之间通信,实现依赖父子进程文件共享,可以把它看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。
函数原型:int pipe(int fd[2]);该函数由参数fd返回两个文件操作符,fd[0]作为读端,fd[1]作为写端(注意:由于是半双工因此不能同时读写,因此在需要读的进程中要close(fd[1]),而在写进程中close(fd[0]))。单个进程中的管道几乎没有任何用处,通常,调用pipe的进程接着调用fork(创建进程)。
注:
(1)当读一个写端已被关闭的管道是,在所有数据都被读取后,read返回0,以指示达到文件结束处。管道的写端彻底关闭(父子进程的写端都得关闭,否则会有进程处于未关闭状态,还在等待写),读端返回一个0,父子进程都得关闭。
(2)如果写一个读端已被关闭的管道,则产生信号SIGPIPE。如果忽略该信号或者捕捉该信号并从处理程序返回,则write返回-1,errno设置为EPIPE。
(3)示例代码见附录1。
2、有名管道
有名管道也叫命名管道,在文件系统目录中存在一个管道文件。管道文件仅仅是文件系统中的标示,并不在磁盘上占据空间。在使用时,在内存上开辟空间,作为两个进程数据交互的通道。
管道文件的创建:
- 在shell中使用mkfifo 命令
mkfifo filename
- mkfifo 函数 (在代码中使用其创建管道文件)
函数原型:int mkfifo(const char *filename,mode_t mode);
使用方式:
- 使用open函数打开管道文件
如果一个进程以只读(只写)打开,那么这个进程会被阻塞到open,直到另一个进程以只写(只读)或者读写打开。
- 使用read函数读取内容
read读取普通文件,read不会阻塞。而read读取管道文件,read会阻塞运行,直到管道中有数据或者所有的写端关闭。
- 使用write函数发送内容,使用close函数关闭打开的文件。
总结:
(1)两个进程运行时,写端彻底关闭,则读端read返回0,也会关闭。
(2)如果管道的读端关闭,继续写入数据会发生异常,写端收到未捕获的信号SIGPIPE会关闭写端。当然也可以修改默认的信号响应方式,比如增加信号处理函数。
(3)写端写入数据以后,读端不从里面读取内容:数据保持在管道中存在一段时间。管道文件的大小是0。
(4)管道通讯发送的数据若没有指定进程接收,任何一个进程只要打开的是同一个管道文件,都有可能读到数据。
(5)read读取管道中的数据,只要读过的数据就会被清空 。
附录一:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
int fd[2];
int ret = pipe(fd);
if (ret == -1)
{
perror("pipe error!");
exit(-1);
}
pid_t pid = fork();
if (pid == -1)
{
perror("fork error!");
exit(-1);
}
else if (pid == 0)//子进程读数据
{
close(fd[1]);
char buf[1024];
ret = read(fd[0], buf, sizeof(buf));
if (ret == 0) printf("------\n");
write(STDOUT_FILENO, buf, ret);
}
else
{
close(fd[0]);// 父进程写数据
char* str = "Hello pipe\n";
write(fd[1], str, strlen(str));
sleep(2);
}
}