管道
1、特点:
它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。
它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。
它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。
管道分为pipe(无名管道)和fifo(命名管道)两种,除了建立、打开、删除的方式不同外,这两种管道几乎是一样的。他们都是通过内核缓冲区实现数据传输。
pipe用于相关进程之间的通信,例如父进程和子进程,它通过pipe()系统调用来创建并打开,当最后一个使用它的进程关闭对他的引用时,pipe将自动撤销。
FIFO即命名管道,在磁盘上有对应的节点,但没有数据块——换言之,只是拥有一个名字和相应的访问权限,通过mknode()系统调用或者mkfifo()函数来建立的。一旦建立,任何进程都可以通过文件名将其打开和进行读写,而不局限于父子进程,当然前提是进程对FIFO有适当的访问权。当不再被进程使用时,FIFO在内存中释放,但磁盘节点仍然存在。
管道的实质是一个内核缓冲区,进程以先进先出的方式从缓冲区存取数据:管道一端的进程顺序地将进程数据写入缓冲区,另一端的进程则顺序地读取数据,该缓冲区可以看做一个循环队列,读和写的位置都是自动增加的,一个数据只能被读一次,读出以后再缓冲区都不复存在了。当缓冲区读空或者写满时,有一定的规则控制相应的读进程或写进程是否进入等待队列,当空的缓冲区有新数据写入或慢的缓冲区有数据读出时,就唤醒等待队列中的进程继续读写。
————————————————
版权声明:本文为CSDN博主「zhaohong_bo」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhaohong_bo/article/details/89552188
无名管道
需要使用到pipe()函数
fd[2]中的两个成员,fd[0]为读端,fd[1]为写端
一般在一开始就会定好两个进程的读写关系,用close(fd[0])或close(fd[1])来关闭不用的读端或者写端
否则就会出现自己写自己读的尴尬状态。
而且不能用open的方法打开已经关闭的读端或者写端,否则会出现问题。
写管道用write()函数的方式写入数据
读管道用read()函数的方式读取数据
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(){
int fd[2];
if(pipe(fd)<0){
perror("pipe error:");
}
pid_t pid;
pid = fork();
char* buf_father;
char* buf_son;
if(pid>0){//father
close(fd[0]);//父进程关闭读端
buf_father = "father love son";
write(fd[1],buf_father,strlen(buf_father));
printf("father write %ld bytes :%s\n",strlen(buf_father),buf_father);
wait();
}
else if(pid == 0){//son
close(fd[1]);//子进程关闭写端
buf_son = (char*)malloc(128);
read(fd[0],buf_son,128);//管道内部如果没有数据就会阻塞在read函数这里
printf("son read %ld bytes :%s\n",strlen(buf_son),buf_son);
exit(0);
}
else{//fork error
perror("fork error:");
}
return 0;
}
命名管道
需要用到mkfifo创建命名管道,命名管道以文件的形式存在,可以用普通文件的方法进行读写
mkfifo在man手册的第三页
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
//pathname:文件名
//mode:权限,一般用0666表示所有用户的可读可写可执行权限
而直接用mkfifo创建命名管道可能会返回错误信息——因为管道文件已存在可能会返回错误信息,根据man手册中的内容我稍微做了下总结
//注意 : 以下的errno需要调用头文件errno.h
ERRORS
EACCES : 文件权限问题,只要有其中一个文件的权限不允许读写就会使得错误码errno = EACCES
EDQUOT : 用户在文件系统上的磁盘块或索引节点的配额已经用完,一般出现在磁盘空间不够的时候errno = EDQUOT
EEXIST : 文件已存在,但是调用了mkfifo就会导致错误码errno = EEXIST
ENAMETOOLONG : 文件名长度超限,错误码errno = ENAMETOOLONG
ENOENT : pathname中的目录组件不存在,或者是一个悬空的符号链接,错误码errno = ENOENT
ENOSPC : 目录或文件系统没有存放新文件的空间,错误码errno = ENOSPC
ENOTDIR :在pathname中用作目录的组件实际上不是目录,错误码errno = ENOTDIR
EROFS : 参数中的文件名为只读的,错误码errno = EROFS
EBADF : dirfd 不是一个有效的文件描述符 //不懂??
ENOTDIR : pathname是一个相对路径,dirfd是一个引用文件而不是目录的文件描述符。
读取端
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#include <fcntl.h>
int main(){
if(mkfifo("./exchange",0600)==-1&&errno!=EEXIST){//调用mkfifo函数失败,且失败原因不是文件已存在。
//目的是即使fifo文件存在也不要报错
perror("mkfifo error:");
}
int fd = open("./exchange",O_RDONLY);
printf("open success\n");
char buf[30];
int n_read;
while(1){
n_read = read(fd,buf,30);
printf("read %d bytes : %s\n",n_read,buf);
}
close(fd);
return 0;
}
发送端
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main(){
int fd = open("./exchange",O_WRONLY);
printf("open success\n");
char* buf = "hello world";
while(1){
write(fd,buf,25);
sleep(1);
}
close(fd);
return 0;
}