详解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等函数

 

 




作者: 柳德维

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

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

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

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

目录
相关文章
|
9月前
|
Web App开发 Linux 程序员
获取和理解Linux进程以及其PID的基础知识。
总的来说,理解Linux进程及其PID需要我们明白,进程就如同汽车,负责执行任务,而PID则是独特的车牌号,为我们提供了管理的便利。知道这个,我们就可以更好地理解和操作Linux系统,甚至通过对进程的有效管理,让系统运行得更加顺畅。
258 16
|
8月前
|
监控 Shell Linux
Linux进程控制(详细讲解)
进程等待是系统通过调用特定的接口(如waitwaitpid)来实现的。来进行对子进程状态检测与回收的功能。
190 0
|
8月前
|
存储 负载均衡 算法
Linux2.6内核进程调度队列
本篇文章是Linux进程系列中的最后一篇文章,本来是想放在上一篇文章的结尾的,但是想了想还是单独写一篇文章吧,虽然说这部分内容是比较难的,所有一般来说是简单的提及带过的,但是为了让大家对进程有更深的理解与认识,还是看了一些别人的文章,然后学习了学习,然后对此做了总结,尽可能详细的介绍明白。最后推荐一篇文章Linux的进程优先级 NI 和 PR - 简书。
252 0
|
8月前
|
存储 Linux Shell
Linux进程概念-详细版(二)
在Linux进程概念-详细版(一)中我们解释了什么是进程,以及进程的各种状态,已经对进程有了一定的认识,那么这篇文章将会继续补全上篇文章剩余没有说到的,进程优先级,环境变量,程序地址空间,进程地址空间,以及调度队列。
151 0
|
8月前
|
Linux 调度 C语言
Linux进程概念-详细版(一)
子进程与父进程代码共享,其子进程直接用父进程的代码,其自己本身无代码,所以子进程无法改动代码,平时所说的修改是修改的数据。为什么要创建子进程:为了让其父子进程执行不同的代码块。子进程的数据相对于父进程是会进行写时拷贝(COW)。
210 0
|
11月前
|
Linux 数据库 Perl
【YashanDB 知识库】如何避免 yasdb 进程被 Linux OOM Killer 杀掉
本文来自YashanDB官网,探讨Linux系统中OOM Killer对数据库服务器的影响及解决方法。当内存接近耗尽时,OOM Killer会杀死占用最多内存的进程,这可能导致数据库主进程被误杀。为避免此问题,可采取两种方法:一是在OS层面关闭OOM Killer,通过修改`/etc/sysctl.conf`文件并重启生效;二是豁免数据库进程,由数据库实例用户借助`sudo`权限调整`oom_score_adj`值。这些措施有助于保护数据库进程免受系统内存管理机制的影响。
|
11月前
|
Linux Shell
Linux 进程前台后台切换与作业控制
进程前台/后台切换及作业控制简介: 在 Shell 中,启动的程序默认为前台进程,会占用终端直到执行完毕。例如,执行 `./shella.sh` 时,终端会被占用。为避免不便,可将命令放到后台运行,如 `./shella.sh &`,此时终端命令行立即返回,可继续输入其他命令。 常用作业控制命令: - `fg %1`:将后台作业切换到前台。 - `Ctrl + Z`:暂停前台作业并放到后台。 - `bg %1`:让暂停的后台作业继续执行。 - `kill %1`:终止后台作业。 优先级调整:
962 5
|
运维 关系型数据库 MySQL
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能
|
弹性计算 Linux 区块链
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
603 4
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
|
算法 Linux 调度
探索进程调度:Linux内核中的完全公平调度器
【8月更文挑战第2天】在操作系统的心脏——内核中,进程调度算法扮演着至关重要的角色。本文将深入探讨Linux内核中的完全公平调度器(Completely Fair Scheduler, CFS),一个旨在提供公平时间分配给所有进程的调度器。我们将通过代码示例,理解CFS如何管理运行队列、选择下一个运行进程以及如何对实时负载进行响应。文章将揭示CFS的设计哲学,并展示其如何在现代多任务计算环境中实现高效的资源分配。