管道
其实可以理解为内核为我们申请的一段内存匿名管道
只能进行具有亲缘关系之间的进程进行通信
调用pipe()函数创建一个管道
int pipe(int pipefd[2]);//参数为一个数组,用于操作系统给管道分配两个文件描述符
pipefd[0]为管道的读端
pipefd[1]为管道的写端
下来看一个例子,实现从标准输入读取数据,写入管道,从管道读取数据,写入标准输入
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main()
{
int pipefd[2];
int pi_ret=pipe(pipefd);
if(pi_ret<0)
{
perror("pipe");exit(1);
}
else
{
char buf_forstdin[1024]={0};
char buf_forpipe[1024]={0};
read(0,buf_forstdin,sizeof(buf_forstdin)-1);//从标准输入读到管道中
write(pipefd[1],buf_forstdin,sizeof(buf_forstdin));//从标准输入的数据写入到管道中
read(pipefd[0],buf_forpipe,sizeof(buf_forpipe)-1);//从管道读取数据
write(1,buf_forpipe,sizeof(buf_forpipe));//将数据写在标准输出上
}
return 0;
}
用fork来共享管道原理
看管道就像是看文件,不能同时读或者同时写,当两个要往管道中读时 应该将管道的写端关闭,当要进行写时应该将读端关闭。
父子进程共同完成上面读写任务
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main()
{
int pipefd[2];
int pi_ret=pipe(pipefd);
if(pi_ret<0)
{
perror("pipe");exit(1);
}
else
{
pid_t pid=fork();
if(pid<0)
{
perror("fork");exit(1);
}
if(pid==0)//子进程往管道中写
{
char buf_forstdin[1024]={0};
read(0,buf_forstdin,sizeof(buf_forstdin)-1);//从标准输入读到管道中
close(pipefd[0]);
write(pipefd[1],buf_forstdin,sizeof(buf_forstdin));//从标准输入的数据写入到管道中
exit(0);
}
else//父进程从管道中读
{
close(pipefd[1]);
char buf_forpipe[1024]={0};
read(pipefd[0],buf_forpipe,sizeof(buf_forpipe)-1);//从管道读取数据
write(1,buf_forpipe,sizeof(buf_forpipe));//将数据写在标准输出上
wait(NULL);
}
}
return 0;
}
再来看管道的几种情况
(1)当管道写满时
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main()
{
int pipefd[2];
int pi_ret=pipe(pipefd);
if(pi_ret<0)
{
perror("pipe");exit(1);
}
else
{
pid_t pid=fork();
if(pid<0)
{
perror("fork");exit(1);
}
if(pid==0)//子进程往管道中写
{
close(pipefd[0]);
const char * str="hello world\n";
int count=0;
while(1)//让子进程一直往管道中写,并打印出写了几次
{
write(pipefd[1],str,strlen(str));//从标准输入的数据写入到管道中
printf("%d \n",count++);
}
exit(0);
}
else//父进程从管道中读
{ // ssize_t size=read(pipefd[0],buf_forpipe,sizeof(buf_forpipe)-1);//从管道读取数据
// if(size>0)
// {
// printf("%d \n",count++);
// buf_forpipe[size]='\0';
// printf("%s",buf_forpipe);
// }
// wait(NULL);
}
}
return 0;
}
扫描二维码关注公众号,回复:
999200 查看本文章
看到子进程将管道写满了就不会再写了
write()调用阻塞,知道有进程读取管道中的数据
(2)管道没有数据可读时
#include <stdio.h> #include <sys/wait.h> #include <unistd.h> #include <string.h> #include <stdlib.h> int main() { int pipefd[2]; int pi_ret=pipe(pipefd); if(pi_ret<0) { perror("pipe");exit(1); } else { pid_t pid=fork(); if(pid<0) { perror("fork");exit(1); } if(pid==0)//子进程往管道中写 { close(pipefd[0]); const char * str="hello world\n"; int count=0; while(1)//让子进程往管道中写 { write(pipefd[1],str,strlen(str));//从标准输入的数据写入到管道中 if(++count==5) {//让子进程只写5 次 sleep(3); break; } sleep(1);//写一次睡一会保证让父进程将一次写入的数据读走 } exit(0); } else//父进程从管道中读 { close(pipefd[1]); char buf_forpipe[1024]={0}; int count=0; while(1) { ssize_t size=read(pipefd[0],buf_forpipe,sizeof(buf_forpipe)-1);//从管道读取数据 if(size>0) { printf("%d \n",count++); buf_forpipe[size]='\0'; printf("%s",buf_forpipe); } } wait(NULL); } } return 0; }
我们看到这里的父进程就阻塞在这里等着读取数据,一直等着有数据为止
上面两种情都没有将读端和写端关闭,只要写满了就必须等有进程将其数据读走,只要没有数据可以读,就必须等有进程将其数据写入,这也体现了进程之间的同步现象
(3)子进程停止写后将管道读端关闭
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>i
nt main()
{
int pipefd[2];
int pi_ret=pipe(pipefd);
if(pi_ret<0)
{
perror("pipe");
exit(1);
}
else
{
pid_t pid=fork();
if(pid<0)
{
perror("fork");
exit(1);
}
if(pid==0)//子进程往管道中写
{
close(pipefd[0]);
const char * str="hello world\n";
int count=0;
while(1)//让子进程往管道中写
{
write(pipefd[1],str,strlen(str));//从标准输入的数据写入到管道中
if(++count==5)
{
//让子进程只写5 次
sleep(3);
close(pipefd[1]);
break;
}
sleep(1);//写一次睡一会保证让父进程将一次写入的数据读走
}
exit(0);
}
else//父进程从管道中读
{
close(pipefd[1]);
char buf_forpipe[1024]={0};
int count=0;
while(1)
{
ssize_t size=read(pipefd[0],buf_forpipe,sizeof(buf_forpipe)-1);//从管道读取数据
if(size>0)
{
printf("%d \n",count++);
buf_forpipe[size]='\0';
printf("%s",buf_forpipe);
}
else if(size==0)
{
printf("read done size :%lu\n",size);
break;
}
}
wait(NULL);
}
}
return 0;
}
这里当子进程停止写将管道读端关闭 ,父进程再去读的时候就会读到文件结束符,返回值为0
(4)进程读完之后将管道读端关闭
#include <stdio.h> #include <sys/wait.h> #include <unistd.h> #include <string.h> #include <stdlib.h> int main() { int pipefd[2]; int pi_ret=pipe(pipefd); if(pi_ret<0) { perror("pipe");exit(1); } else { pid_t pid=fork(); if(pid<0) { perror("fork");exit(1); } if(pid==0)//子进程往管道中写 { close(pipefd[0]); const char * str="hello world\n"; while(1)//让子进程往管道中写 { write(pipefd[1],str,strlen(str));//从标准输入的数据写入到管道中 sleep(1);//写一次睡一会保证让父进程将一次写入的数据读走 } exit(0); } else//父进程从管道中读 { close(pipefd[1]); char buf_forpipe[1024]={0}; int count=0; while(1) { ssize_t size=read(pipefd[0],buf_forpipe,sizeof(buf_forpipe)-1);//从管道读取数据 if(size>0) { buf_forpipe[size]='\0'; printf("%s",buf_forpipe); printf("%d \n",++count); if(count==4) { close(pipefd[0]); break; } } } int status; wait(&status); printf("signal:%d\n",status&0x7f);//打印子进程的退出码 } } return 0; }
上面代码让子进程一直在往管道中写,父进程读取4次后将管道读端关闭,这时如果再尝试进行写入的时候系统就会将进程异常终止,这里把终止信号打印出来为13
总结以下:
当没有数据可以读时,read()调用阻塞,一直等到有数据来为止
当管道写满时,write()调用阻塞,一直等到有数据来读
当将管道的所有写端文件描述符关闭,则read()返回值为0.(读到文件结束标志)
当将管道的所有读端文件描述符关闭,则write()操作会产生SIGPIPE信号,进程退出