zoukankan      html  css  js  c++  java
  • 详解linux进程间通信-管道 popen函数 dup2函数

      前言:进程之间交换信息的唯一方法是经由f o r ke x e c传送打开文件,或通过文件系统。本章将说明进程之间相互通信的其他技术—I P CInterProcess Communication)。今天将介绍半双工的管道。

      一、匿名管道

      1、匿名管道介绍:

       管道有两种限制;
      (1) 它们是半双工的。数据只能在一个方向上流动。
      (2)
    它们只能在具有公共祖先的进程之间使用。通常,一个管道由一个进程创建,然后该进程调用f o r k,此后父、子进程之间就可应用该管道。 

      管道是由调用p i p e函数而创建的:
      #include <unistd.h>
      int pipe(intf i l e d e s [ 2 ]) ;
      返回:若成功则为0,若出错则为 - 1

      经由参数f i l e d e s返回两个文件描述符: f i l e d e s [ 0 ]为读而打开, f i l e d e s [ 1 ]为写而打开。 f i l e d e s [ 1 ]
    的输出是f i l e d e s [ 0 ]的输入。

      程序1- 1创建了一个从父进程到子进程的管道,并且父进程经由该管道向子进程传送数据。

    #include "my.h"
    
    int main()
    {
        int pfd[2],ret;
        ret = pipe(pfd);//创建管道
        if(ret<0)
        {   
            perror("pipe error!");
            exit(-1);
        }   
        pid_t pid = fork();
        if(pid<0)
        {   
            perror("fork error!");
            exit(-1);
        }   
        else if(pid>0)//父进程
        {   
            close(pfd[0]);
            int num;
            puts("please input your num:");
            scanf("%d",&num);
            write(pfd[1],&num,sizeof(num));
            //wait(NULL);
        }   
        else //子进程
        {   
            close(pfd[1]);
            int num;
            read(pfd[0],&num,sizeof(num));
            printf("num:%d
    ",num);
        }
        return 0;
    }

      注:头文件my.h见这篇博客:http://www.cnblogs.com/liudw-0215/p/8946879.html 

      运行示例,如下图:

      

      接下来介绍几个跟管道有关的函数。

      2、dupd u p 2函数  

      下面两个函数都可用来复制一个现存的文件描述符:

      #include <unistd.h>

      int dup(intf i l e d es) ;
      int dup2(int f i l e d e s, int f i l e d e s 2) ;
      两函数的返回:若成功为新的文件描述符,若出错为- 1
      由d u p返回的新文件描述符一定是当前可用文件描述符中的最小数值。用 d u p 2则可以用f i l e d e s 2
      参数指定新描述符的数值。如果 f i l e d e s 2已经打开,则先将其关闭。如若f i l e d e s等于f i l e d e s 2,则
      d u p 2返回f i l e d e s 2
    ,而不关闭它。

      优化程序1-1,程序1-2如下:

    #include "my.h"
    
    int main()
    {
        int pfd[2],ret;
        ret = pipe(pfd);
        if(ret<0)
        {   
            perror("pipe error!");
            exit(-1);
        }   
        pid_t pid = fork();
        if(pid<0)
        {   
            perror("fork error!");
            exit(-1);
        }   
        else if(pid>0)
        {   
            close(pfd[0]);
            int num;
            puts("please input your num:");
            scanf("%d",&num);
            write(pfd[1],&num,sizeof(num));
            wait(NULL);
        }   
        else
        {   
            close(pfd[1]);
            dup2(pfd[0],STDIN_FILENO);//pfd[0]复制到标准输入
            int num;
            read(STDIN_FILENO,&num,sizeof(num));
            printf("num:%d
    ",num);
        }
        return 0;
    }

      

    3、popenp c l o s e函数  

     因为常见的操作是创建一个连接到另一个进程的管道,然后读其输出或向其发送输入,所

    以标准I / O库为实现这些操作提供了两个函数 p o p e np c l o s e。这两个函数实现的操作是:创建
    一个管道, f o r k一个子进程,关闭管道的不使用端, e x e c一个s h e l l以执行命令,等待命令终止。
      #include <stdio.h>
      FILE *popen(const char * c m d s t r i n g, const char * t y p e) ;
      返回:若成功则为文件指针,若出错则为 N U L L
      
      int pclose(FILE * f p) ;
      返回: c m d s t r i n g的终止状态,若出错则为 - 1
      函数popen 先执行f o r k,然后调用e x e c以执行c m d s t r i n g,并且返回一个标准 I / O文件指针。
      如果t y p e"r",则文件指针连接到c m d s t r i n g的标准输出
      如果t y p e "w",则文件指针连接到c m d s t r i n g 的标准输入

      程序1-3将用popen函数实现下图功能:

      

      

    #include "my.h"
    
    int main()
    {
        int c;
        while((c = getchar()) != EOF)
        {   
            if(isupper(c))  //是否有大写
                c = tolower(c);  //转为小写
            if(putchar(c) == EOF)
                puts("put error!");
            if(c == '
    ')
                fflush(stdout);  //刷新标准输出
        }   
        exit(0);
    }

    ---isupper.c---

      

    #include "my.h"
    #define MAXLINE 4096
    
    int main()
    {
        char line[MAXLINE];
        FILE *fpin;
    
        if((fpin = popen("./upper","r")) == NULL)
            perror("popen error!");
    
        for(;;){
            fputs("prompt > ",stdout);
            fflush(stdout);
            if(fgets(line,MAXLINE,fpin) == NULL)
                break;
            if(fputs(line,stdout) == EOF)
                perror("puts error!");    
        }   
        if(pclose(fpin) == -1) 
            perror("pclose error!");
    
        putchar('
    ');
        return 0;
    
    }

    ---popen.c---

      运行演示如下图:

     二、有名管道(命名管道)

      1、简介

      命名管道有时被称为FIFO。管道只能由相关进程使用,它们共同的祖先进程创建了管道。
    但是,通过F I F O,不相关的进程也能交换数据。
    #include <sys/types.h>
    #include <sys/stat.h>
    int mkfifo(const char * p a t h n a m e, mode_tm o d e) ;
    返回:若成功则为0,若出错则为 - 1
    m k f i f o函数中m o de参数的规格说明与o p e n函数中的m o d e相同
    一旦已经用 m k f i f o创建了一个 F I F O,就可用 o p e n打开它。确实,一般的文件 I / O函数
    c l o s er e a dw r i t eu n l i n k等)都可用于F I F O

      程序2-1演示有名管道通信,一个写端、一个读端:

    #include "my.h"
    
    typedef struct{
        char name[16];
        int age;
        double height;
    }Person;
    
    int main()
    {
        mkfifo("pipe",0644);//创建管道 名字为pipe
        int fd = open("pipe",O_WRONLY);
        if(fd < 0)
        {   
            perror("open error!");
            exit(-1);    
        }   
        
        Person p;
        puts("please input your name,age,height:");
        scanf("%s%d%lf",p.name,&p.age,&p.height);
        write(fd,&p,sizeof(p));
        close(fd);
    
        return 0;
    }
    ---fwrite.c---
    #include "my.h"
    
    typedef struct{
        char name[16];
        int age;
        double height;
    }Person;
    
    int main()
    {
        int fd = open("pipe",O_RDONLY);
        if(fd < 0)
        {   
            perror("open error!");
            exit(-1);
        }   
    
        Person p;
        read(fd,&p,sizeof(p));
        printf("name:%-5sage:%-5dheight:%-5lf
     ",p.name,p.age,p.height);
        close(fd);
       unlink("pipe");//删除管道文件
    return 0; }
    --- fread.c ---

      运行演示:先编译fwrite.c生成w可执行用户,./w执行,再编译fread.c然后执行,写端输入数据,读端输出数据:

    总结:主要介绍了进程间通信的管道,主要分为匿名管道和有名管道,还介绍了popen等函数




  • 相关阅读:
    AIBigKaldi(十三)| Kaldi的三音子模型训练(下)(源码解析)
    AIBigKaldi(十二)| Kaldi的三音子模型训练(中)(源码解析)
    AIBigKaldi(十一)| Kaldi的三音子模型训练(上)(源码解析)
    AIBigKaldi(十)| Kaldi的thchs30实例(源码解析)
    AIBigKaldi(九)| Kaldi的解码搜索(源码解析)
    AIBigKaldi(八)| Kaldi的解码图构造(下)(源码解析)
    AIBigKaldi(七)| Kaldi的解码图构造(上)(源码解析)
    AIBigKaldi(六)| Kaldi的单音子模型训练(下)(源码解析)
    AIBigKaldi(五)| Kaldi的单音子模型训练(上)(源码解析)
    理解 Linux 的硬链接与软链接
  • 原文地址:https://www.cnblogs.com/liudw-0215/p/8952609.html
Copyright © 2011-2022 走看看