zoukankan      html  css  js  c++  java
  • 匿名管道

    在讨论匿名管道之前,我们先回顾下read和write系统调用以及fgets函数

    #include <unistd.h>
    
    ssize_t read(int fd, void *buf, size_t count);

    当read成功时,返回值为读到的字符数。当遇见文件结尾时,返回0(也就是什么都读不出来了)。出错,返回-1。—> 参考 man 2 read

    #include <unistd.h>
    
    ssize_t write(int fd, const void *buf, size_t count);

    当write成功时,返回值为写入的字符数。返回0代表什么也没有写入。出错返回-1。 –> 参考 man 2 write

    #include <stdio.h>
    
    char *fgets(char *s, int size, FILE *stream);

    fgets()  reads  in  at most one less than size characters from stream and stores them into the buffer pointed to by s.  Reading stops after an EOF or a newline.  If a newline  is read, it is stored into the buffer.  A '' is stored after the last character in the buffer.
    返回值:遇见文件结尾或发生错误返回NULL。

    由于匿名管道使用的是文件描述符,所以我们只能用read和write对其进行读写。因为标准读写函数都是基于文件指针的。

    匿名管道代码如下

    /*************************************************************************
      > File Name: my_pipe.c
      > Author: KrisChou
      > Mail:zhoujx0219@163com 
      > Created Time: Thu 21 Aug 2014 09:13:24 PM CST
     ************************************************************************/
    
    #include<stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    int main(int argc, char* argv[])
    {
        int fds[2];//fds[0]-r   fds[1] - w //writ(fds[1])   read(fds[0])
        if(pipe(fds) == -1)
        {
            perror("pipe");
            exit(1);
        }
        
        if(fork() == 0)//child read
        {
            printf("child : %u 
    ", getpid());
            close(fds[1]);          /* 关闭子进程的写 */
            char buf[1024]= "";
            while(1)
            {
                memset(buf, 0, 1024);
                if(read(fds[0], buf, 1024) == 0)
                {
                    break ;
                }
                printf("child : read : %s
    ", buf);
            }
            printf("child exit ! 
    ");
            exit(1);
        }else // parent write
        {
            printf("parent : %u 
    ", getpid());
            close(fds[0]);         /* 关闭父进程的读 */
            char buf[1024];
            while(memset(buf,0, 1024), fgets(buf, 1024, stdin) != NULL)
            {
                write(fds[1], buf, strlen(buf));    
            }
    
            close(fds[1]);         /* c+d后退出循环,当父进程不准备读了,要关闭父进程的写 */
            printf("parent bread while! 
    ");
            wait(NULL);
        }
    
        return 0 ;
    }

    程序输出为:

    [purple@localhost test_class]$ vim my_pipe.c
    [purple@localhost test_class]$ gcc my_pipe.c
    [purple@localhost test_class]$ ./a.out
    parent : 2829
    child : 2830
    how are you
    child : read : how are you
    
    baby U R beautiful
    child : read : baby U R beautiful
    
    parent bread while!
    child exit !

    我们来分析下这个程序。观察程序运行结果,首先输出的是parent:2829,显然先执行的是父进程,之后一直执行到父进程的while循环,由于循环中有fgets,其为阻塞函数,此时内核把时间片给子进程。

    子进程打印出child:2830后一直往下执行,直到while循环,在read处阻塞,等待输入,此时时间片到达父进程,父进程write后(注意由于fgets会接受换行符,因此每次write都会刷新缓冲区),子进程就读。

    直到父进程按c+d退出while循环,关闭父进程的写,wait挂起后,将时间片给子进程。由于写端关闭,子进程read函数必定读不到东西,退出循环,执行exit,退出,同时exit唤醒父进程,父进程退出。

    注意:如果父进程退出循环后,但不关闭其写端,子进程会一直等待输入,不会认为自己读不到东西。

    将代码稍作变化

    在关闭子进程的写端语句后,再加上一条关闭子进程的读端语句,如下:

    if(fork() == 0)//child read
        {
            printf("child : %u 
    ", getpid());
            close(fds[1]);          /* 关闭子进程的写 */
            close(fds[0]);          /* 关闭子进程的读 */  /* 新加的语句 */
            char buf[1024]= "";
            while(1)
            {
                memset(buf, 0, 1024);
                if(read(fds[0], buf, 1024) == 0)
                {
                    break ;
                }
                printf("child : read : %s
    ", buf);
            }
            printf("child exit ! 
    ");
            exit(1);
        }

    此时,执行到子进程的while循环后,会一直不断输出:

    child : read :
    child : read :
    child : read :
    child : read :
    child : read :
    child : read :
    child : read :
    child : read :
    child : read :
    child : read :
    child : read :
    child : read :
    child : read :
    child : read :
    child : read :
    child : read :
    ..
     程序进入死循环。原因是当子进程的读端已经关闭后,再读管道,必然出错,也就是此时read调用的返回值是-1。因此不断执行打印语句,陷入死循环。

    改动后完整代码

    /*************************************************************************
      > File Name: my_pipe.c
      > Author: Comst
      > Mail:750145240@qq.com 
      > Created Time: Thu 21 Aug 2014 09:13:24 PM CST
     ************************************************************************/
    
    #include<stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    int main(int argc, char* argv[])
    {
        int fds[2];//fds[0]-r   fds[1] - w //writ(fds[1])   read(fds[0])
        if(pipe(fds) == -1)
        {
            perror("pipe");
            exit(1);
        }
        
        if(fork() == 0)//child read
        {
            printf("child : %u 
    ", getpid());
            close(fds[1]);          /* 关闭子进程的写 */
            close(fds[0]);          /* 关闭子进程的读 */
            char buf[1024]= "";
            while(1)
            {
                memset(buf, 0, 1024);
                if(read(fds[0], buf, 1024) == -1)
                {
                    break ;
                }
                printf("child : read : %s
    ", buf);
            }
            printf("child exit ! 
    ");
            exit(1);
        }else // parent write
        {
            printf("parent : %u 
    ", getpid());
            close(fds[0]);         /* 关闭父进程的读 */
            char buf[1024];
            while(memset(buf,0, 1024), fgets(buf, 1024, stdin) != NULL)
            {
                write(fds[1], buf, strlen(buf));    
            }
    
            close(fds[1]);         /* c+d后退出循环,当父进程不准备读了,要关闭父进程的写 */
            printf("parent bread while! 
    ");
            wait(NULL);
        }
    
        return 0 ;
    }

    程序运行结果1:

    [purple@localhost test_class]$ ./a.out
    parent : 3560
    child : 3561
    child exit !
    baby you are beautiful

    当程序执行到父进程的while循环时,我们输入“baby you are beautiful”,由于此时子进程的读端已经关闭,我们再往写端写东西,内核直接发信号,挂掉我们的程序。即输入“baby you are beautiful”后,程序直接退出。

    程序运行结果2:

    [purple@localhost test_class]$ ./a.out
    parent : 3588
    child : 3589
    child exit !
    parent bread while!

    当程序执行到父进程的while循环时,我们输入 c+d,此时正常退出循环,接着执行下面的语句。

  • 相关阅读:
    MFC中L, _T(),TEXT,_TEXT区别以及含义
    Qt5完美解决 界面显示中文乱码
    TCP三次握手四次挥手详解
    TCP 长连接与短连接的区别
    Servlet 生命周期、工作原理
    Java反射的三种实现方式
    apache httpclient 4.5 兼容 http https
    mysql千万级大数据SQL查询优化
    Java String字符串补0或空格
    mysql存储过程
  • 原文地址:https://www.cnblogs.com/jianxinzhou/p/3928800.html
Copyright © 2011-2022 走看看