进程间通信----管道(一)

管道

其实可以理解为内核为我们申请的一段内存

匿名管道

只能进行具有亲缘关系之间的进程进行通信

调用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信号,进程退出

猜你喜欢

转载自blog.csdn.net/misszhoudandan/article/details/80328627