开发者社区> 2020柳> 正文

详解linux进程间通信-管道 popen函数 dup2函数

简介:   前言:进程之间交换信息的唯一方法是经由f o r k或e x e c传送打开文件,或通过文件系统。本章将说明进程之间相互通信的其他技术—I P C(InterProcess Communication)。
+关注继续查看

  前言:进程之间交换信息的唯一方法是经由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\n",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\n",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 == '\n')
            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('\n');
    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\n ",p.name,p.age,p.height);
    close(fd);
   unlink("pipe");//删除管道文件
return 0; }
--- fread.c ---

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

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

 

 




作者:柳德维

-------------------------------------------

个性签名:独学而无友,则孤陋而寡闻。做一个灵魂有趣的人!

如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!

万水千山总是情,打赏一分行不行,所以如果你心情还比较高兴,也是可以扫码打赏博主,哈哈哈(っ•̀ω•́)っ⁾⁾!

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
【Android 逆向】Android 进程注入工具开发 ( 注入代码分析 | 获取 linker 中的 dlopen 函数地址 并 通过 远程调用 执行该函数 )
【Android 逆向】Android 进程注入工具开发 ( 注入代码分析 | 获取 linker 中的 dlopen 函数地址 并 通过 远程调用 执行该函数 )
27 0
写一个函数对字符串数组排序,使所有变位词都相邻
题目 写一个函数对字符串数组排序,使得所有的变位词都相邻。 解答 首先,要弄清楚什么是变位词。变位词就是组成的字母相同,但顺序不一样的单词。 比如说:live和evil就是一对变位词。OK,那么这道题目的意思就很清楚了, 它并不要求我们将字符串数组中的字符串按字典序排序,否则我们直接调用STL中的sort 函数就可以了。
842 0
ML之catboost:catboost的CatBoostRegressor函数源代码简介、解读之详细攻略
ML之catboost:catboost的CatBoostRegressor函数源代码简介、解读之详细攻略
412 0
详解C++ 编写String 的构造函数、拷贝构造函数、析构函数和赋值函数
详解C++ 编写String 的构造函数、拷贝构造函数、析构函数和赋值函数
65 0
Linux下C编程,子进程创建函数fork() 执行解析
最近在看进程间的通信,看到了fork()函数,虽然以前用过,这次经过思考加深了理解。现总结如下: 1.函数本身   (1)头文件   #include  #include   (2)函数原型   pid_t fork( void);  (pid_t 是一个宏定义,其实质是int 被定义在#include中)  返回值: 若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1   (3)函数说明   一个现有进程可以调用fork函数创建一个新进程。
770 0
【Android 逆向】Android 进程注入工具开发 ( EIP 寄存器指向 dlopen 函数 | ESP 寄存器指向栈内存 | 调试程序收回目标进程控制权 )
【Android 逆向】Android 进程注入工具开发 ( EIP 寄存器指向 dlopen 函数 | ESP 寄存器指向栈内存 | 调试程序收回目标进程控制权 )
30 0
+关注
2020柳
梦想还是要有的,万一见鬼了呢
25
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
JS零基础入门教程(上册)
立即下载
性能优化方法论
立即下载
手把手学习日志服务SLS,云启实验室实战指南
立即下载