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

 

管道

理解为内核为我们申请的一段内存

匿名管道

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

 

调用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;
}

 

看到子进程将管道写满了就不会再写了

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

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);
                    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

 

总结如下:

  1. 匿名管道只能用于具有亲缘关系的进程之间通信
  2. 管道提供流式服务,可以分多次读写
  3. 生命周期随进程(文件描述符)
  4. 内置同步互斥机制
  5. 同步:当没有数据可以读时,read()调用阻塞,一直等到有数据来为止;当管道写满时,write()调用阻塞,一直等到有数据来读
  6. 互斥:不能同时进行读或者同时进行写数据
  7. 半双工通信,数据只能一个方向流动,若需要全双工通信,就需要建立两个管道
  8. 当将管道的所有写端文件描述符关闭,则read()返回值为0.(读到文件结束标志EOF)
  9. 当将管道的所有读端文件描述符关闭,则write()操作会产生SIGPIPE信号,进程退出

 

命名管道:

  1. 匿名管道由pipe函数创建并打开
  2. 命名管道由mkfifo函数创建,打开open
  3. FIFO(命名管道)于pipe(匿名管道)之间唯一的区别在于他们创建与打开方式不同

完。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值