管道
- 进程间通信的方式之一
- 本质是对内核缓冲区的读写,存储在一个环形队列中
- 管道对应的内核缓冲区的大小是固定的,默认为4KB
- 管道分为读写两端,数据从写端进入管道,从读端流出管道
- 管道中的数据只能读一次,相当于出队列
- 管道是单工的,数据单向流动,从写端到读端
- 对管道的读写操作都是阻塞的
- 读管道:当管道中没有数据,读操作阻塞;有数据阻塞解除
- 写管道:写满后,写操作阻塞;有空闲后,继续写入
管道的分类
匿名管道:只能实现血缘关系的进程间通信
创建匿名管道
#include <unistd.h>
int pipe(int fd[2]);
-
传出参数:
- fd[0]—>读端
- fd[1]—>写端
-
返回值: 成功 0 ;失败 -1
匿名管道通信
- 子进程执行shell命令
ps aux
,父进程将结果显示到终端
//要关闭管道的一部分,形成唯一的单向流通线路
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <fcntl.h>
int main(void)
{
int fd[2];
int ret = pipe(fd);
if(ret == -1)
{
perror("pipe error!");
return -1;
}
ret = fork();
if(ret == 0) //children
{
//close read
close(fd[0]);
dup2(fd[1],STDOUT_FILENO);
execlp("ps","ps","aux",NULL);
}
else if(ret > 0 )//parent
{
close(fd[1]);
char buf[1024];
while(1)
{
bzero(buf,sizeof(buf));
ret = read(fd[0],buf,sizeof(buf));
if(ret == 0)
break;
printf("%s-->%d\n",buf,ret);
}
close(fd[0]);
wait(NULL);
}
else{
perror("fork error");
return -1;
}
return 0;
}
有名管道
创建有名管道
- 有名管道文件大小永远为 0,因为有名管道也是将数据存储到内存的缓冲区中
- 有名管道可以在血缘关系的进程中使用,也可以在没有血缘关系的进程中使用
命令的方式
mkfifo 管道名
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const chra *pathname,mode_t mode);
- pathname 创建有名管道的名字
- mode 文件的权限
- 最终权限
mode & ~umask
- 返回值: 成功 0;失败 -1
进程间通信
- 需要通过open操作得到读写管道的文件描述符,如果读写端只有一端打开,进程会阻塞在open函数,知道另一个进程打开对应端。
//有名管道进行写操作
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(void)
{
//创建有名管道
int ret = mkfifo("./fifotest",0664);
if(ret == -1)
{
perror("mkfifo error!");
exit(-1);
}
printf("管道文件创建成功!\n");
int ret_open = open("./fifotest",O_WRONLY);
if(ret_open == -1)
{
perror("open fifo fail!");
exit(-1);
}
//循环写入文件
int i = 0;
while(i<100)
{
char buf[4096]={0};
sprintf(buf,"hello world %d",i+1);
write(ret_open,buf,strlen(buf)); //如果只有写端,没有读端会阻塞的
i++;
sleep(1);
}
close(ret_open);
exit(0);
}
/*
设置为非阻塞的形式
// 通过fcntl 修改就可以, 一般情况下不建议修改
// 管道操作对应两个文件描述符, 分别是管道的读端 和 写端
// 1. 获取读端的文件描述符的flag属性
int flag = fcntl(fd[0], F_GETFL);
// 2. 添加非阻塞属性到 flag中
flag |= O_NONBLOCK;
// 3. 将新的flag属性设置给读端的文件描述符
fcntl(fd[0], F_SETFL, flag);
// 4. 非阻塞读管道
char buf[4096];
read(fd[0], buf, sizeof(buf));
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(void)
{
int fd_r = open("./fifotest",O_RDONLY);
if(fd_r == -1)
{
perror("open fifotest error!");
exit(-1);
}
//循环读取
char buf[4096];
while (1)
{
memset(buf,0,sizeof(buf));
int ret = read(fd_r,buf,sizeof(buf));
if(ret == 0)
{
printf("已经读完,阻塞解除!\n");
break;
}
printf("%s--->%d\n",buf,ret);
}
close(fd_r);
exit(0);
}
pipe调用别的可执行文件
//fdfs_upload_file
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include "make_log.h"
int main(int argc,char *argv[])
{
//create pipe
int fd[2];
int ret = pipe(fd);
if(ret == -1)
{
LOG("pipe","create pipe error","ret = %d",ret);
return -1;
}
ret = fork();
if(ret == 0)//children
{
close(fd[0]);
dup2(fd[1],STDOUT_FILENO);
execlp("fdfs_upload_file","fdfs_upload_file","/etc/fdfs/client.conf",argv[1],NULL);
}
else if(ret > 0)//parent
{
close(fd[1]);
char buf[1024] ;
while(1)
{
bzero(buf,sizeof(buf));
ret = read(fd[0],buf,sizeof(buf));
if(ret == 0)
break;
printf("%s",buf);
}
close(fd[0]);
wait(NULL);
}
else{
LOG("fork","fork error","fork ret = %d",ret);
return -1;
}
LOG("interface","interface ok!","return 0");
return 0 ;
}