管道
一. 管道
管道:一个进程来凝结到另一个进程的一个数据流称为一个”管道”。
管道实际上就是一个固定大小的缓冲区,就是一种文件。并且都只是内核当中的一段内存,管道只是一个入口,若是没有进程打开该内存,则该管道就不会创建。
管道又分为:
- 匿名管道
- 命名管道
二. 匿名管道
匿名管道是适用于有亲缘关系的进程之间的通信。
这里的fd是一个数组。一个有两个元素的数组,一个是fd[0]是管道的读端,fd[1]是管道的写端。
- 写:是先将要传送的数据写入到缓冲区中,再将缓冲区的内容写入到管道之中。
读:是将管道当中的数据先读到缓冲区当中,再将缓冲区中的数据打印到文件或者标准输出上。
管道在读的时候,发现没有可读的数据,有两种处理方法:- O_NONBLOCK disable:当前进程阻塞,就是一直在等,等到有数据可以读取。
- O_NONBLOCK enable:直接返回-1,errno值为EAGAIN。
当管道写满之后,也是有两种处理方法:
- O_NONBLOCK disable:阻塞式等待,等到有进程将管道中的数据读走。
- O_NONBLOCK enable:直接返回-1,errno值为EAGAIN。
- 若是所有管道写端对应的文件描述符被关闭,则read返回0。
- 如果所有的管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE,进而可能导致write进程退出
- 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性
- 当写入的数据量大于PIPE_BUF时,linux不保证写入的原子性
具体的匿名管道的实现代码如下:
//匿名管道的创建与使用
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define ERR_EXIT(m)\
do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
int main()
{
//1.创建管道
int fd[2];
char buf1[1024] = {0};
if(pipe(fd) == -1)
{
perror("pipe");
exit(1);
}
//2.创建子进程
pid_t pid = fork();
if(pid < 0)
{
perror("fork");
ERR_EXIT("fork error");
}
//3.父进程写数据到管道
if(pid > 0)
{
close(fd[0]); //关闭管道的读端
fgets(buf1,100,stdin);
write(fd[1],buf1,strlen(buf1));
close(fd[1]);
exit(EXIT_SUCCESS);
}
//4. 子进程从管道中读数据并输出到屏幕上
if(pid == 0)
{
close(fd[1]);
char buf[10] = {0};
read(fd[0],buf,6);
printf("%s\n",buf);
}
return 0;
}
这段代码主要实现的功能就是:父进程向管道中通过标准输入输入数据,子进程再通过这个管道读取到输入的数据并打印到屏幕上。
匿名管道的特点:
- 只能用来具有亲缘关系的进程之间的通信,通常,管道由一个进程创建,然后再进行fork,此后,父子进程直接就可以应用该管道。
- 通道提供流式服务。
- 进程退出,管道就会被释放,管道的生命周期随进程的生命周期
- 内核会对管道操作惊醒同步与互斥(写满了就不写了,读完了就不读了)。
- 管道是半双工的,数据只能向一个方向流动,不可以两个方向同时工作。若需要双方通信,则就需要建立两个管道。
三. 命名管道(FIFO)
命名管道是一种特殊类型的文件,也是文件的中的一种类型。
创建命名管道有两种方式:
- 命令行上创建
mkfifo filename
- 可以在程序中创建
int mkfifo(const char* filename,mode_t mode);
命名管道与匿名管道的区别:
- 匿名管道由pipe函数创建并打开
- 命名管道由mkfifo函数创建,打开用open
- FIFO与pipe之间唯一的区别就在它们的创建与打开方式不同,在创建打开之后,两个管道就是相同的了。
命名管道也有相应的打开规则:
读管道时:
- O_NONBLOCK disable:阻塞直到有相应进程为写而打开该FIFO
- O_NONVLOCK enable:立刻返回成功
写管道时:
- O_NONBLOCK disable:阻塞直到有相应进程为读而打开该FIFO
- O_NONBLOCK enable:立刻返回失败,错误码为ENXIO
使用mkfifo函数来创建一个命名管道:
创建FIFO,将文件当中的数据写入到FIFO中
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//定义一个宏函数
#define ERR_EXIT(m)\
do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
int main()
{
mkfifo("tp",0644);
int infd;
infd = open("abc",O_RDONLY);
if(infd < 0)
ERR_EXIT("open");
int outfd;
outfd = open("tp",O_WRONLY);
if(outfd < 0)
ERR_EXIT("open");
char buf[1024];
int n;
while((n = read(infd,buf,1024)) > 0)
{
write(outfd,buf,n);
}
close(infd);
close(outfd);
return 0;
}
从FIFO中读取数据,写入到另一个文件当中
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define ERR_EXIT(m)\
do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
int main()
{
int infd,outfd;
outfd = open("back",O_CREAT | O_WRONLY | O_TRUNC,0644);
if(outfd < 0)
ERR_EXIT("open");
infd = open("tp",O_RDONLY);
if(infd < 0)
ERR_EXIT("open");
char buf[1024];
int n;
while((n = read(infd,buf,1024)) > 0)
{
write(outfd,buf,n);
}
close(infd);
close(outfd);
unlink("tp");
return 0;
}
上面两段程序通过FIFO管道实现了将一个文件中的内容拷贝到另一个文件当中。