linux进程间通信之管道篇

本文是对http://www.cnblogs.com/andtt/articles/2136279.html管道一节的进一步阐释和解释

1 管道

1.1 管道简介

管道是unix系统IPC的最古老的形式,并且所有的unix系统都提供此种通信机制;它有下面两种局限性
  • 它的工作方式是半双工,即数据只能在一个方向上流动;即只能一个进程写,另一个进程读或者一个进程读,另一个进程写;如果想要两个进程同时读写,则必须建立两个管道
  • 它们只能在具有公共祖先的进程之间使用;通常情况下是父进程建立管道,然后fork出子进程,父子进程使用该管道进行通信(有名管道没有此局限)

1.2 管道相关系统调用

#include <unistd.h>
int pipe(int filedes[2])
函数功能:
创建管道
参数说明:
通过filedes返回两个文件描述符,filedes[0]用来读数据,filedes[1]用来写数据
返回值说明:
创建成功返回0,创建失败返回-1并置errno,可通过perror打印错误信息

1.3 管道通用模型及读写规则

通常情况下,进程在调用pipe建立管道后接着调用fork,创建了父进程和子进程之间的管道;而数据的流向取决于我们在fork之后的操作,如果数据从父进程流向子进程,则父进程关闭管道读端fd[0];子进程关闭写端fd[1];反之,如果数据从子进程流向父进程,则父进程关闭写段fd[1],子进程关闭读端fd[0]。数据从父进程流向子进程的模型图如下

1.3.1 从管道中读取数据

  • 如果管道的写端关闭,且管道中的数据已经读取完成,则read返回0
  • 如果管道的写段存在,读取是如果请求的字节数目大于PIPE_BUF,则返回管道中现有的字节数;如果请求的字节数目不大于PIPE_BUF(/usr/include/linux/limites.h),则返回管道中的现有数据(管道中的数据<请求的数据)或者请求的字节数(管道中的数据>请求的数据)
规则1验证程序:
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
int main(void)
{
    int pipe_fd[2];
    pid_t pid;
    char r_buf[100];
    char w_buf[4];
    char* p_wbuf;
    int r_num;
    int cmd;
 
    memset(r_buf,0,sizeof(r_buf));
    memset(w_buf,0,sizeof(r_buf));
    p_wbuf=w_buf;
    if(pipe(pipe_fd)<0)
    {
       printf("pipe create error\n");
       return -1;
    }
 
    if((pid=fork())==0)
    {
        printf("\n");
        close(pipe_fd[1]);
        sleep(3);//等待父进程关闭写端
        r_num=read(pipe_fd[0],r_buf,100);
        printf("read num is %d  the data read from the pipe is %d\n",r_num,atoi(r_buf));
        close(pipe_fd[0]);
        exit(0);
    }else if(pid>0)
    {
        close(pipe_fd[0]);//read
        strcpy(w_buf,"111");
        if(write(pipe_fd[1],w_buf,4)!=-1)
            printf("parent write over\n");
        close(pipe_fd[1]);//write
        printf("parent close fd[1] over\n");
        sleep(10);
        exit(0);
    }

    return 0;
} 
输入结果如下
parent write over
parent close fd[1] over

read num is 4   the data read from the pipe is 111
<pre name="code" class="cpp">read num is 0   the data read from the pipe
 
  

说明当写段关闭后,如果管道中还有数据未被读取,则读端继续读取管道中的数据,读取完后,再次读取,则返回0

规则2验证

/*系统PIPE_BUF=4096*/
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
int main(void)
{
    int pipe_fd[2];
    pid_t pid;
    char r_buf[5000];
    char w_buf[5000];
    char* p_wbuf;
    int r_num;
    int cmd;

    memset(r_buf,0,sizeof(r_buf));
    memset(w_buf,0,sizeof(r_buf));
    p_wbuf=w_buf;
    if(pipe(pipe_fd)<0)
    {
       printf("pipe create error\n");
       return -1;
    }
    if((pid=fork())==0)
    {
        printf("\n");
        close(pipe_fd[1]);
        r_num=read(pipe_fd[0],r_buf,5000);
        printf("read num is %d the data read from the pipe\n",r_num);
        sleep(2);
        r_num=read(pipe_fd[0],r_buf,3000);
        printf("read num is %d the data read from the pipe\n",r_num);
        sleep(2);
        r_num=read(pipe_fd[0],r_buf,2000);
        printf("read num is %d the data read from the pipe\n",r_num);
        sleep(2);
        close(pipe_fd[0]);
        exit(0);
    }
    else if(pid>0)
    {
       close(pipe_fd[0]);//read
       memset(w_buf, '1', 4096);
       write(pipe_fd[1], w_buf, 4096); 
       printf("1write child to read\n");
       sleep(2);
       memset(w_buf, '2', 4096);
       write(pipe_fd[1], w_buf, 4096); 
       printf("2write child to read\n");
       sleep(4);

       close(pipe_fd[1]);//write
          printf("parent close fd[1] over\n");
       sleep(10);
       exit(0);
   }    
   return 0;
}
输出结果如下

1write child to read

read num is 4096 the data read from the pipe
2write child to read
read num is 3000 the data read from the pipe
read num is 1096 the data read from the pipe
parent close fd[1] over
父进程写4096个字节,子进程读5000个字节,大于PIPE_BUF,返回管道中现有的数据4096个字节

父进程写4096个字节,子进程读3000个字节,管道中的数据>请求的数据,所以返回请求字节数:3000个字节

此时管道中还有1096个字节,子进程读2000个字节,管道中的数据<请求的数据,所以返回管道中现有的字节数:1096个字节

1.3.2 向管道中写数据

  • 如果读端关闭,此时向管道中写数据将收到SIGPIPE信号,应用程序可以处理该信号,也可以忽略,默认终止应用程序
  • 读端不关闭,写端写入数据时,不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走管道中的数据,那么写操作将一直阻塞
规则1验证
#include <unistd.h>
#include <sys/types.h>
int main(void)
{
    int pipe_fd[2];
    pid_t pid;
    char r_buf[4];
    char* w_buf;
    int writenum;
    int cmd;

    memset(r_buf,0,sizeof(r_buf));
    if(pipe(pipe_fd)<0)
    {
       printf("pipe create error\n");
       return -1;
    }
 
    if((pid=fork())==0)
    {
        close(pipe_fd[0]);
        close(pipe_fd[1]);
        sleep(10);      
        exit(0);
    }
    else if(pid>0)
    {
       sleep(1);  //等待子进程完成关闭读端的操作
       close(pipe_fd[0]);//write
       w_buf="111";
       if((writenum=write(pipe_fd[1],w_buf,4))==-1)
              printf("write to pipe error\n");
       else 
              printf("the bytes write to pipe is %d \n", writenum);
       close(pipe_fd[1]);
    }    
}
输出结果为Brokenpipe;因为父子进程都关闭了读端,写端向管道中写数据,收到了系统发出的SIGPIPE信号
规则2验证
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>

int main(void)
{
    int pipe_fd[2];
    pid_t pid;
    char r_buf[4096];
    char w_buf[4096*2];
    int writenum;
    int rnum;

    memset(r_buf,0,sizeof(r_buf));    
    if(pipe(pipe_fd)<0)
    {
       printf("pipe create error\n");
       return -1;
    }
    if((pid=fork())==0)
    {
        close(pipe_fd[1]);
        while(1)
        {
           sleep(1); 
           rnum=read(pipe_fd[0],r_buf,1000);
           printf("child: readnum is %d\n",rnum);
        }
        close(pipe_fd[0]);
        exit(0);
    }
    else if(pid>0)
    {
        close(pipe_fd[0]);//write
        memset(r_buf,0,sizeof(r_buf));    
        if((writenum=write(pipe_fd[1],w_buf,1024))==-1)
            printf("write to pipe error\n");
        else 
            printf("the bytes write to pipe is %d \n", writenum);
        writenum=write(pipe_fd[1],w_buf,4096);
        close(pipe_fd[1]);
        exit(0)
    }
    return 0;
}
输入结果如下
the bytes write to pipe is 1024     //父进程写入1000字节后,继续向管道中写,写入4096-1024字节后,阻塞,等待读取管道中数据
xlzh@cmos:~$ child: readnum is 1000  
child: readnum is 1000
child: readnum is 1000
child: readnum is 1000
child: readnum is 1000
child: readnum is 120
child: readnum is 0
...

2 有名管道

待续













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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值