进程间通信---管道

1.进程间通信(IPC):

把数据从用户空间拷到内核缓冲区,另一个进程再从内核缓冲区将数据读走,这种由内核提供的机制叫做进程间通信。

2.管道

管道是一种最基本IPC机制,由pipe函数创建

3.创建管道的函数pipe( )

int pipe( pipefd[2] );
①函数调用成功时返回0,失败返回-1

②函数分别以读方式和写方式打开一个文件,使用3,4作为文件描述符;

③pipefd[0]指向读管道,保存读描述符;
pipefd[1]指向写管道,保存写描述符;

4.使用管道实现进程间通信的步骤

①父进程创建管道,得到两个文件描述符指向管道;

②父进程fork( )出子进程,子进程也会有两个文件描述符;

③父进程关闭读端pipefd[0],子进程关闭写端pipefd[1],即父子进程指向一个公共管道;

5.实现代码:

#include<unistd.h>
#include<stdio.h>
#include<string.h>

int main()
{
   int pipefd[2]={0,0};
   if(pipe(pipefd)<0)//父进程创建管道
   {
       perror("pipe");
       return 1;
   }
   pid_t pid=fork();//父进程fork( )出子进程
   if(pid<0)
   {
       perror("fork");
       return 2;
   }
   else if(pid==0)
   {//child
       close(pipefd[0]);//关闭子进程的读端
       int i=0;
       char* msg="Hello,i am child\n";
       while(i<100)
       {
           write(pipefd[1],msg,strlen(msg)+1);
           sleep(1);
           i++;
       }
   }
   else
   {//father
       close(pipefd[1]);//关闭父进程的写端
       char buffer[100];
       int j=0;
       while(j<100)
       {
           if((read(pipefd[0],buffer,sizeof(buffer)))>0)
           {  
                printf("father#%s",buffer);
           }
           j++;
       }
   }
}

结果如图:
子进程向管道中写“hello,i am child”,父进程从管道中读取到这句话,打印出来;

这里写图片描述

6.管道通信的特点:

①只能进行单向通信;

②只能为有血缘关系的进程间通信;

③管道通信依赖文件系统,生存周期随进程,即进程退出时,管道也退出;

④管道进行数据通信时,按数据流读写;

⑤管道自带同步机制;

7.管道的四种情况
(1)当关闭了管道的所有写端时,但仍然有进程要从管道的读端读数据,所以管道中的剩余数据都被读取后,再次读取时会返回0.

#include<unistd.h>
#include<stdio.h>
#include<string.h>

int main()
{
   int pipefd[2]={0,0};
   if(pipe(pipefd)<0)
   {
       perror("pipe");
       return 1;
   }
   pid_t pid=fork();
   if(pid<0)
   {
       perror("fork");
       return 2;
   }
   else if(pid==0)
   {//child
       close(pipefd[0]);
       int i=0;
       char* msg="Hello,i am child";
       while(i<10)
       {
           write(pipefd[1],msg,strlen(msg)+1);
           sleep(1);
           i++;
       }
       close(pipefd[1]);
   }
   else
   {//father
       close(pipefd[1]);
       char buffer[100];
       int j=0;
       while(j<100)
       {
           memset(buffer,'\0',sizeof(buffer));
           int ret=read(pipefd[0],buffer,sizeof(buffer));
           printf("father#%s,ret=%d\n",buffer,ret);
           j++;
       }
   }
   return 0;
}

结果如图所示:
读完10次后,返回0
这里写图片描述

(2)当指向管道的写端没关闭,而写端进程也没有向管道中写数据,但此时有进程从管道读端读数据,那么管道中剩余数据都被读完时,再次读取会被阻塞;

#include<unistd.h>
#include<stdio.h>
#include<string.h>

int main()
{
   int pipefd[2]={0,0};
   if(pipe(pipefd)<0)
   {
       perror("pipe");
       return 1;
   }
   pid_t pid=fork();
   if(pid<0)
   {
       perror("fork");
       return 2;
   }
   else if(pid==0)
   {//child
       close(pipefd[0]);
       int i=0;
       char* msg="Hello,i am child";
       while(i<20)
       { 
          if(i<10)
          {
             write(pipefd[1],msg,strlen(msg)+1);
          }
          sleep(1);
          i++;
       }
       close(pipefd[1]);
   }
   else
   {//father
       close(pipefd[1]);
       char buffer[100];
       int j=0;
       while(j<20)
       {
           memset(buffer,'\0',sizeof(buffer));
           int ret=read(pipefd[0],buffer,sizeof(buffer));
           printf("father#%s,ret=%d\n",buffer,ret);
           j++;
       }
   }
   return 0;
}

结果如图所示:
这里写图片描述

(3)当管道的所有读端被关闭,此时有进程向写端写入数据,此时会收到信号SIGPIPE,导致进程异常终止;

#include<unistd.h>
#include<stdio.h>
#include<string.h>

int main()
{
   int pipefd[2]={0,0};
   if(pipe(pipefd)<0)
   {
       perror("pipe");
       return 1;
   }
   pid_t pid=fork();
   if(pid<0)
   {
       perror("fork");
       return 2;
   }
   else if(pid==0)
   {//child
       close(pipefd[0]);
       int i=0;
       char* msg="Hello,i am child";
       while(i<20)
       { 
          if(i<10)
          {
             write(pipefd[1],msg,strlen(msg)+1);
          }
          sleep(1);
          i++;
       }
       close(pipefd[1]);
   }
   else
   {//father
       close(pipefd[1]);
       char buffer[100];
       int j=0;
       while(j<3)
       {
           memset(buffer,'\0',sizeof(buffer));
           int ret=read(pipefd[0],buffer,sizeof(buffer));
           printf("father#%s,ret=%d\n",buffer,ret);
           j++;
       }
       close(pipefd[0]);
       sleep(10);
   }
   return 0;
}

结果如图所示:
这里写图片描述

(4)当指向管道的读端未关闭,而且进程也没有读数据,此时有进程向写端写数据,那么管道被写满时,再次写入会被阻塞;

#include<unistd.h>
#include<stdio.h>
#include<string.h>

int main()
{
   int pipefd[2]={0,0};
   if(pipe(pipefd)<0)
   {
       perror("pipe");
       return 1;
   }
   pid_t pid=fork();
   if(pid<0)
   {
       perror("fork");
       return 2;
   }
   else if(pid==0)
   {//child
       close(pipefd[0]);
       int i=0;
       char* msg="Hello,i am child";
       while(i<20)
       { 
          if(i<10)
          {
             write(pipefd[1],msg,strlen(msg)+1);
          }
          sleep(1);
          i++;
       }
       close(pipefd[1]);
   }
   else
   {//father
       close(pipefd[1]);
       char buffer[100];
       int j=0;
       while(j<0)
       {
           memset(buffer,'\0',sizeof(buffer));
           int ret=read(pipefd[0],buffer,sizeof(buffer));
           printf("father#%s,ret=%d\n",buffer,ret);
           j++;
       }
       close(pipefd[0]);
       sleep(10);
   }
   return 0;
}

结果如图所示:
这里写图片描述

8.命名管道(FIFO)与匿名管道的区别

上面所描述的管道都为匿名管道;
①命名管道可以实现两个毫不相关的进程间通信,而匿名管道只能实现有血缘关系的进程间通信;

② 命名管道允许在硬盘上进行文件创建,而匿名管道不允许;

③命名管道可以通过命令创建,也可以通过函数mkfifo( )创建,匿名管道通过pipe( )函数创建;

④命名管道是一个存在于硬盘上的文件,而匿名管道是存在于内存的特殊文件;

⑤使用命名管道前,必须先调用open( )将它打开;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值