• Linux进程间通信-匿名管道


    前面我们讲了进程间通信的一种方式,共享内存。下面看一看另一种机制,匿名管道。
    1.什么是管道
    管道是一个进程的数据流到另一个进程的通道,即一个进程的数据输出作为另一个进程的数据输入,管道起到了桥梁的作用。
    比如,在shell中输入命令:ls -l|grep string,ls和grep是两个进程,"|"符号表示管道,意思是执行ls -l进程,并将输出结果result_1,作为grep string进程的输入result_0,grep进程将result_0中存在字符串string的信息打印到屏幕。

    2.管道的使用
    1)popen函数:启用一个新进程,并可以向它传递数据,或者通过它接受数据。

    FILE *popen(const char *command,conse char *open_mode);

    command:运行的程序名和参数
    open_mode:有两个值"r(只读)","w(只写)"
          "r":可以获取新进程的输出
          "w":可以向新进程发送数据
    返回值:返回输入输出文件流指针

    2)pclose函数:关闭输入输出文件流指针
    若调用该函数时,新进程仍然在运行,则pclose将等待,直至新进程结束。
    返回值:返回新进程的退出码。

    3.popen函数使用示例
    下例循环读取read_fp输出文件流的内容,写入write_fp的输入文件流,直到输出流内容读完。

    #include<stdlib.h>
    #include<stdio.h>
    #include<string.h>
    int main()
    {
        FILE *read_fp = NULL;
        FILE *write_fp = NULL;
        char buffer[BUFSIZ+1];
        int chars_read = 0;
    
        //初始化缓冲区
        memset(buffer,'',sizeof(buffer));
        read_fp = popen("ls -l","r");
        write_fp = popen("grep rwxrwxr-x","w");
        if(read_fp && write_fp)
        {
            
            chars_read = fread(buffer,sizeof(char),BUFSIZ,read_fp);
            while(chars_read)
            {
                buffer[chars_read]='';
                //把数据写入grep进程
                fwrite(buffer,sizeof(char),chars_read,write_fp);
                chars_read = fread(buffer,sizeof(char),BUFSIZ,read_fp);
            }
            //关闭文件流
            pclose(read_fp);
            pclose(write_fp);
            exit(EXIT_SUCCESS);
        }
        printf("%d
    ",2);
        exit(EXIT_FAILURE);
    }

    输出结果:

    3、popen的原理及优缺点
    当调用popen运行一个新进程时,它首先启动shell,然后将command参数传递给它
    优点:可以使用shell来分析命令字符串,启动非常复杂的shell命令。
    缺点:不仅要启动一个新进程,还要启动一个shell,效率会比较低。

    4.pipe函数的使用

    int pipe(int file_description[2]);

    file_description[2]:表示管道的输出输入端,输出端数据经过管道流到输入端,函数执行完后, 会将这个数组赋值。
              file_description[1]表示管道输出端文件描述符
              file_description[0]表示管道输入端文件描述符
    返回值:0成功,-1失败

    与popen不同的是,pipe函数是一个底层调用,不会启动shell。
    popen是使用文件流(FILE)工作的,pipe使用的是文件描述符,相应的数据要用底层的read和write来读取和发送。

    5.pipe函数使用示例
    下例中,我们在父进程中创建一个管道,然后调用fork创建一个子进程。
    此时,父进程的file_description[1]输出端,对应着子进程file_description[0]的输入端。
    数据通过管道由父进程传到子进程。示例如下:

    #include<stdlib.h>
    #include<stdio.h>
    #include<string.h>
    int main()
    {
        int data_processed = 0;
        const char data[]="Hello pipe!";
        char buffer[BUFSIZ+1];
        pid_t pid;
        memset(buffer,'',sizeof(buffer));
        int filedes[2];
        if(pipe(filedes)==0)
        {
            //创建管道成功
            //fork子进程
            pid=fork();
            if(pid==-1)
            {
                fprintf(stderr,"Fork failure");
                exit(EXIT_FAILURE);
            }
            if(pid==0)
            {
                data_processed = read(filedes[0],buffer,BUFSIZ);
                printf("read %d bytes:%s
    ",data_processed,buffer);
                exit(EXIT_SUCCESS);
            }
            else
            {
                data_processed = write(filedes[1],data,strlen(data));
                printf("wrote %d bytes:%s
    ",data_processed,data);
                exit(EXIT_SUCCESS);
            }
        }
        exit(EXIT_FAILURE);
    }

    输出结果:

    6.管道用作标准输入和输出
    我们知道标准的输入描述符为0,输出描述符为1,
    为了使用已经定义好的标准程序,如od命令,从标准输入读入数据。
    需要将管道的输入端描述符置为0,此时,我们需要用到一个辅助函数dup

    dup函数:创建一个描述符,复制原有描述符参数的结构到新建的描述符。

    int dup(int file_descriptor);

    新的描述符规则是,使用最小的可用值。

    要想使管道的输入描述符为标准输入描述符,我们可以先关闭文件描述符0,然后调用dup,
    此时新建的描述符即为最小可用值0,标准输入描述符。

    close(0);
    dup(file_description[0]);

    上例使用标准输入描述符改造后的示例如下:

    #include<stdlib.h>
    #include<stdio.h>
    #include<string.h>
    int main()
    {
        int data_processed = 0;
        const char data[]="Hello pipe!";
        int filedes[2];
        pid_t pid;
        if(pipe(filedes)==0)
        {
            pid = fork();
            if(pid==-1)
            {
                fprintf("stderr","fork failure!
    ");
                exit(EXIT_FAILURE);
            }
            if(pid==0)
            {
                close(0);
                dup(filedes[0]);
                close(filedes[0]);
                close(filedes[1]);
                execlp("od","od","-c",0);
                exit(EXIT_FAILURE);
            }
            else
            {
                close(filedes[0]);
                data_processed = write(filedes[1],data,strlen(data));
                close(filedes[1]);
                printf("wrote %d bytes:%s
    ",data_processed,data);
            }
        }
    }

    输出结果:

    7.匿名管道需要注意的问题
    1)当管道没有关闭时,若没有数据可读,read调用会阻塞
    2)当管道关闭时,read调用会返回0
    3)匿名管道通信,进程间必须是父子关系

  • 相关阅读:
    使用脚本进入一个命令行控制台,并预设执行的命令列表
    cifs挂载远程文件出现 No such device or address错误
    longtable 跨越多个页面时,如何在跨页时自动断行并加上横线及去掉页眉
    matplotlib中文显示-微软雅黑
    latex编译过程-关于嵌入所有字体
    python做图笔记
    linux启动全过程
    连接并同步windows下的git仓库
    反向ssh
    Ubuntu更改启动内存
  • 原文地址:https://www.cnblogs.com/shijingjing07/p/5620646.html
Copyright © 2020-2023  润新知