一、无名管道
管道通常是指无名管道,(因为没有文件名,所以叫无名管道)是 UNIX 系统IPC最古老的形式。
特点:
1、它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。
2、它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。
3、它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。
4、管道中的数据被读走了,管道中就没有数据了。
原理
当建立一个管道时,由参数fd返回两个文件描述符:fd[0]为读而打开,fd[1]为写而打开,fd[1]的输出是fd[0]的输入,管道是建立在内核之中的
如:
如果要关闭管道,只需要将fd[0]和fd[1]的文件描述符关闭即可
创建
单个进程中的管道几乎没有任何用处。所以,调用pipe的进程接着调用fork,这样就创建了从父进程到子进程的IPC通道。 父子进程都有读端和写端,子进程的内容都是从父进程复制过来的 。
对于从父进程到子进程的管道,父进程关闭管道的读端(fd[0]),子进程则关闭写端(fd[1]),反之也是一样
头文件
#include <unistd.h>
函数原型
int pipe(int pipefd[2]);
返回值:成功返回0,失败返回-1
例如
创建由父进程到子进程的管道,父进程写入hello world,子进程读取数据到buf,然后打印输出
#include <stdio.h>
#include <unistd.h>
#include <string.h>
//int pipe(int pipefd[2]);
int main()
{
int fd[2];
int pid;
char buf[128] = {0};
if(pipe(fd) == -1){ //等于-1表示失败
printf("creat pipe fail\n");
}
pid = fork(); //创建进程
if(pid < 0){ //创建进程失败
printf("creat pid fail\n");
}
else if(pid > 0){ //进入父进程
printf("this is father\n");
close(fd[0]); //关闭读
write(fd[1],"hello world",strlen("hello world")); //往fd[1]写字符串hello world
}
else{ //进入子进程
printf("this is child\n");
close(fd[1]); //关闭写
read(fd[0],buf,128); //将父进程中写入的字符串从fd[0]中读出来
printf("read father %s\n",buf); //打印
}
return 0;
}
二、有名管道(FIFO)
FIFO可以在无关的进程之间交换数据,与无名管道不同
有名管道也叫命名管道,是在文件系统目录中存在一个管道文件中
管道文件仅仅是文件系统中的标示,并不在磁盘上占据空间。在使用时,在内存上开辟空间,作为两个进程数据交互的通道。
管道文件的创建:
#include <sys/types.h>
#include <sys/stat.h> //头文件
int mkfifo(const char *pathname, mode_t mode); //函数原型
mode和open函数中的相似(0600)可读可写open
pathname:路径名字
当创建了一个FIFO,就可以用一般的文件操作它,当打开一个FIFO时,要看是否设置了非阻塞标志(O_NONBLOCK)
如果没有指定O_NONBLOCK(默认没有设置),只读open要阻塞到某个其他进程为写而打开此FIFO(文件),类似的,只写open要阻塞到某个其他进程为读而打开它。
如果指定了O_NONBLOCK,则只读open立即返回,而只写open将出错返回-1,如果已经没有进程为读而打开该FIFO,其errno置ENXIO。(如果管道存在,也会出错)
创建一个管道
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
if((mkfifo("./file",0600) == -1) && errno != EEXIST){ //创建管道
printf("mkfifom fail\n");
perror("why");
}
可以看到生成了一个可读可写的文件,这个文件就是创建的管道
示例代码
一个文件是将数据写入管道,另一个文件是将第一个写入管道中的数据读出来,然后输出
将管道中的文件读到buf中,再输出
read.c
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
//int mkfifo(const char *pathname, mode_t mode);
int main()
{
char buf[30] = {0};
int nread = 0;
int cnt = 0;
if((mkfifo("./file",0600) == -1) && errno != EEXIST){ //创建管道
printf("mkfifom fail\n");
perror("why");
}
int fd = open("./file",O_RDONLY); //以只读方式打开管道
printf("open success\n");
while(1){
nread = read(fd,buf,30); //将管道中的数据读到buf中
printf("read %d byte read file:%s\n",nread,buf); //打印字节数和读出的内容
cnt++;
if(cnt == 5){
break;
}
}
close(fd); //关闭管道
return 0;
}
write.c
向管道里面写数据
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
int main()
{
int cnt = 0;
char *str = "hello world"; //要向管道写入的字符串
int fd = open("./file",O_WRONLY); //以只写的方式打开管道
printf("write open success\n");
while(1){ //连续输入5个
write(fd,str,strlen(str)); //将字符串str写入管道fd
sleep(1); //延时一秒
cnt++;
if(cnt == 5){
break;
}
}
close(fd); //关闭管道
return 0;
}
这里如果运行read.c的话会造成read堵塞,因为只读open要阻塞到某个其他进程为写而打开此管道,也就是说这里要运行write时,read才不会堵塞,
点击运行read.c
可以看到发生了阻塞,然后再运行write.c
read成功的读到数据
如果想开始不堵塞的话
int fd = open("./file",O_RDONLY | O_NONBLOCK);