• Linux学习笔记24——进程管道


    一 管道的作用

      通常把一个进程的输出通过管道连接到另一个进程的输入。

    二 popen和pclose函数

    #include <stdio.h>
    
    FILE *popen(const char *command,      //是要运行的程序名和相应的参数
           const
    char *open_mode      //必须是“r”或者“w”,如果是其它值,errno将返回EINVAL
           );                 
    int pclose(FILE *stream_to_close);     

      popen() 函数通过创建一个管道,调用 fork 产生一个子进程,执行一个 shell 以运行命令来开启一个进程。

      pclose()调用只在popen启动的进程结束后才返回,如果调用pclose函数时它仍在运行,pclose调用将等待该进程的结束。

    例:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    
    int main(){
        FILE *read_fp;          //要读取的文件描述符
        char buffer[BUFSIZ+1];      //用来存储读到的文件信息
        int chars_read;          //实际读取的元素个数
        
        memset(buffer,'',sizeof(buffer));    //将数组清零初始化
        read_fp=popen("cat test*.c | wc -l","r");  //创建管道,用于显示所有test*.c文件的字数
        
        if(read_fp!=NULL){
            chars_read=fread(buffer,sizeof(char),BUFSIZ,read_fp);//从一个文件流中读数据,最多读取count个元素,每个元素size字节,如果调用成功返回实际读取到的元素个数,如果不成功返回 0
            while(chars_read>0){
                buffer[chars_read-1]='';      //清除回车符
                printf("Reading:-
     %s
    ",buffer);
                chars_read=fread(buffer,sizeof(char),BUFSIZ,read_fp);
            }
            pclose(read_fp);        //关闭管道
            exit(EXIT_SUCCESS);
        }
        exit(EXIT_FAILURE);
    }

      使用shell的一个不太好的影响:针对每个popen调用,不仅要启动一个被请求的程序,还要启动一个shell,即每个popen调用将多启动两个进程。从节省系统资源的角度来看,popen函数的调用成本略高,而且对目标命令的调用比正常方式要慢一些。

    三 pipe函数

    #include <unistd.h>
    int pipe(int fd[2]);

      pipe函数的参数是一个由两个整数类型的文件描述符组成的数组的指针,两个返回的文件描述符以一种特殊的方式连接起来,写到fd[1]的所有数据都可以从fd[0]读回来,数据基于先进先出的原则(FIFO)进程处理。

      对一个已关闭写数据的管道做read调用将返回0而不是阻塞,读取无效的文件描述符将看作是一个错误并返回-1
     
      如果跨越fork调用使用管道,就会有两个不同的文件描述符可以用于向管道写数据,一个在父进程中,一个在子进程中。只有把父子进程中的针对管道的写文件描述符都关闭,管道才会被认为是关闭了,对管道的read调用才会失败。

    管道的读写规则:

      1 从管道中读取数据

    • 如果管道的写端不存在,则认为已经读到了数据的末尾,读函数返回的读出字节数为0;
    • 当管道的写端存在时,如果请求的字节数目大于PIPE_BUF,则返回管道中现有的数据字节数,如果请求的字节数目不大于 PIPE_BUF,则返回管道中现有数据字节数(此时,管道中数据量小于请求的数据量);或者返回请求的字节数(此时,管道中数据量不小于请求的数据 量)。注:(PIPE_BUF在include/linux/limits.h中定义,不同的内核版本可能会有所不同。Posix.1要求 PIPE_BUF至少为512字节,red hat 7.2中为4096)。

      2 从管道中写入数据

      向管道中写入数据时,linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将一直阻塞。
      注:只有在管道的读端存在时,向管道中写入数据才有意义。否则,向管道中写入数据的进程将收到内核传来的SIFPIPE信号,应用程序可以处理该信号,也可以忽略(默认动作则是应用程序终止)。

    例子:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    
    int main(){
        const char some_data[]="123";
        int file_pipes[2];
        int data_processed;
        pid_t fork_result;
        
        if(pipe(file_pipes)==0){
            fork_result=fork();
            if(fork_result==(pid_t)-1){
                fprintf(stderr,"Fork failure");
                exit(EXIT_FAILURE);
            }
            if(fork_result==0){      //子进程
                close(0);          //关闭标准输入,即键盘输入
                dup(file_pipes[0]);    //复制一个文件描述符
                close(file_pipes[0]);    //关闭读操作
                close(file_pipes[1]);   //关闭写操作
                
                execlp("od","od","-c",(char*)0);  //利用od查看特殊格式的文件内容,-c表示ASCII字符或反斜杠序列,(char*)0参数作用是终止被调用程序的参数列表
                exit(EXIT_FAILURE);
            }
            else{     //主进程
                close(file_pipes[0]);  
                data_processed=write(file_pipes[1],some_data,strlen(some_data));  //写入数据
                close(file_pipes[1]);
                printf("%d - wrote %d bytes
    ",getpid(),data_processed);
            }
        }
        exit(EXIT_SUCCESS);
    }

      

  • 相关阅读:
    jQuery的简单实用的25个知识点
    toDoList案例
    缓动动画函数的封装
    轮播图的设置:
    Django学习:cookie和session
    Django学习:ORM
    Django学习:中间件
    Django学习:url路由系统
    Django学习:模板继承和配置静态文件
    Django学习:模板语法
  • 原文地址:https://www.cnblogs.com/zjzsky/p/3518604.html
Copyright © 2020-2023  润新知