有名管道:可以用在任意两个或者多个进程之间
是一种特殊的文件,它的路径名存在于文件系统中,通过
mkfifo命令可以创建管道文件,
命令:mkfifo myfifo
管道文件在磁盘上只有i节点,没有数据块,也不保存数据
本质是内核维护的缓存区,实现进程间通信。
#include<stdio.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<unistd.h>
#include<string.h>
int main()
{
//创建有名管道文件
printf("%d进程:创建有名管道\n",getpid());
if(mkfifo("./fifo",0664) == -1)
{
perror("mkfifo");
return -1;
}
//打开管道文件
printf("%d进程:打开管道文件\n",getpid());
int fd = open("./fifo",O_WRONLY);
if(fd == -1)
{
perror("open");
return -1;
}
//写文件
printf("%d进程:发送数据\n",getpid());
while(1)
{
char buf[128];
//通过键盘获取数据
fgets(buf,sizeof(buf),stdin);
//退出条件
if(strcmp(buf,"!\n") == 0)
{
perror("strcmp");
break;
}
//将数据写入管道文件
if (write(fd,buf,strlen(buf)) == -1)
{
perror("write");
return -1;
}
}
//关闭有名管道文件
printf("%d进程:关闭有名管道文件\n",getpid());
close(fd);
//删除文件
printf("%d进程:删除有名管道文件\n",getpid());
unlink("./fifo");
return 0;
}
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/stat.h>
int main()
{
//打开有名管道文件
int fd = open("./fifo",O_RDONLY,0664);
if(fd == -1)
{
perror("open");
return -1;
}
//读取数据
for(;;)
{
char buf[128]= {};
//读取数据
ssize_t size = read(fd,buf,sizeof(buf)-sizeof(buf[0]));
if(size == -1)
{
perror("read");
return -1;
}
if(size == 0)
{
printf("%d进程:对方关闭了管道\n",getpid());
break;
}
//显示数据
printf("%s",buf);
}
//关闭有名管道文件
close(fd);
return 0;
}
无名管道:父子进程或者兄弟进程(共有一个父进程的两个子进程)
无名管道是一个与文件系统无关的内核对象,主要用于父子
进程之间的通信,需要用专门的系统调用函数创建
1、本质也是内核维护的缓存区,调用pipe函数创建无名管道对象,通过该
函数的输出参数pipefd,获得分别用于读写该管道的文件描述符
pipefd[0](读)和pipefd[1](写)。
2、父进程调用fork()函数,创建子进程,子进程复制父进程的文件描述
符表,因此子进程同样持有分别用于读写该管道的两个文件描述符pipefd[0]
和pipefd[1]。
3、负责写数据的进程关闭无名管道对象的读端文件符,负责写数据的关闭
该管道的写端描述符
4、父子进程通过无名管道对象以半双工的方式传输数据。如果需要在父子
进程之间实现双向通信,较一般化的做法是创建两个管道,一个父流向子,
一个子流向父。
5、父子进程分别关闭自己所持有的写端或读端文件描述符。在与一个无名
管道对象相关联的所有文件描述符都被关闭以后,该无名管道对象即从系统
内核销毁
注意:不用的读写端先关闭,
//无名管道
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>
int main()
{
//父进程创建无名管道
printf("%d父进程:",getpid());
int fds[2];
if(pipe(fds)== -1)
{
perror("pipe");
return -1;
}
printf("fds[0]=%d,fds[1]=%d\n",fds[0],fds[1]);
//创建子进程
pid_t pid =fork();
if(pid == -1)
{
perror("fork");
return -1;
}
//子进程读取数据
if(pid == 0)
{
printf("关闭子进程写端\n");
close(fds[1]);
while(1)
{
//读取数据
char buf[128]={};
ssize_t size = read(fds[0],buf,sizeof(buf)-sizeof(buf[0]));
if(size == -1)
{
perror("read");
return -1;
}
if(size == 0)
{
printf("对方关闭了写端\n");
break;
}
printf("%s",buf);
}
printf("关闭读端\n");
close(fds[0]);
return 0;
}
//父进程写入数据
printf("父进程关闭读端\n");
close(fds[0]);
printf("%d父进程发送数据\n",getpid());
while(1)
{
char buf[128] ={};
fgets(buf,sizeof(buf),stdin);
if(strcmp(buf,"!\n") == 0)
{
break;
}
if(write(fds[1],buf,strlen(buf)) == -1)
{
perror("write");
return -1;
}
}
//回收子进程僵尸
close(fds[1]);
if(wait(NULL) == -1)
{
perror("wait");
return -1;
}
return 0;
}
管道符号|:shell进程调用进程A,A调用pipe创建无名管道,改变A文件描述符的
写端为A的pipefd[1],子进程A创建子进程B,改变B的读端为pipefd[0],子进程A和
子进程B分别调用exec函数创建新进程a,b。
a进程的所有输出通过写端进入管道,而b进程的所有输入都来自管道的读端。