Linux进程间通信之管道

news2024/11/9 3:56:44

进程间通信介绍:

进程间通信的概念:

进程间通信简称IPC(Interprocess communication),进程间通信就是在不同进程之间传播或交换信息。

进程间通信的目的:

数据传输: 一个进程需要将它的数据发送给另一个进程。
资源共享: 多个进程之间共享同样的资源。
通知事件: 一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件,比如进程终止时需要通知其父进程。
进程控制: 有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

进程间通信的本质:

进程间通信的本质就是让不同的进程看到同一份资源。

进程间通信的发展:

管道
System V进程间通信
POSIX进程间通信

进程间通信的分类: 

管道

  • 匿名管道
  • 命名管道

System V IPC

  • System V 消息队列
  • System V 共享内存
  • System V 信号量

POSIX IPC

  • 消息队列
  • 共享内存
  • 信号量
  • 互斥量
  • 条件变量
  • 读写锁

管道:

认识管道:

由于进程间具有独立性,想要实现进程间通信非常困难,想要实现进程间通信就必须借助第三方资源,让两个需要通信的进程都可访问这个第三方资源,早期管道就是这样的第三方资源来实现进程间通信。

管道是Unix中最古老的进程间通信的形式。
我们把从一个进程连接到另一个进程的一个数据流称为一个“管道“

演示:

先来介绍两个命令:

1.who

who指令可以用来显示当前云服务器登录的用户数,一行显示一个用户。

如图所示现在有2个用户。 

 2.wc

wc指令可以查指定文件的计算文件的Byte数、字数、或是列数,若不指定文件名称、或是所给予的文件名为"-",则wc指令会从标准输入设备读取数据

wc加上-l指令,计算指定文件的行数。

将上述两个命令通过管道连接,就可以更准确地查出当前云服务器的登录用户:

who进程将数据写入管道,wc从管道中读取到数据,-l指令计算数据的行数,从而得出当前云服务器的登录数。 

匿名管道:

匿名管道性质: 

匿名管道仅支持父子间进程通信。

当我们创建一个进程,在linux系统中它被如下图进行管理:

我们再通过这个进程创建一个子进程,子进程继承父进程的代码和数据:

 没错,此时我们的父子进程能看到同一份资源,我们可以模拟一下通信,父进程往缓冲区写入,子进程往缓冲区读取,早期的工程师发现了这种现象,并且认为这是一种很好的进程间通信的方法,就在这种方法的基础上进行了一下改动,创造了管道。

注意:

我们在进程间通信时,是没必要对磁盘中的文件进行操作的,所以我们的管道没必要与磁盘中的文件产生关联。

文件级缓冲区是由操作系统来维护的,所以当父进程对其写入时,是不会发生写时拷贝的。

pipe函数:

int pipe(int pipefd[2]);

 pipe函数的参数是一个输出型参数,数组pipefd中的两个元素分别用来返回管道读端和写端的文件描述符:

数组元素含义
pipefd[0]管道读端文件描述符
pipefd[1]管道写端文件描述符

 匿名管道的使用:

注意下图中的fd均指pipefd。

1.父进程用pipe函数创建管道。

2.父进程通过fork函数创建子进程。

3.假设我们让子进程写,父进程读,所以我们要关闭不用的文件描述符,父进程关闭写端,子进程关闭读端。

 我们再站在文件描述符的角度深入理解:

匿名管道测试: 

现在用下述代码测试匿名管道,父进程进行一直读取,子进程进行一直写入:

#include <iostream>
#include <cerrno>
#include <cstring>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

const int size = 1024;

//子进程进行写入
void SubProcessWrite(int wrd)
{
    std::string message = "father,i am your childen process! ";
    while(true)
    {
        sleep(1);
        std::cout<<"childen begin write........."<<std::endl;
        static int cent = 0;
        pid_t id = getpid();
        //拼接消息
        std::string info = message;
        info += "my pid is ";
        info += std::to_string(id);
        info += ", cent: ";
        info += std::to_string(cent);
        //写入
        write(wrd,info.c_str(),info.size());
        cent++;
    }
}
//父进程进行读取
void FatherProcessReader(int rfd)
{
    char inbuffer[size];
    while(true)
    {
        std::cout<<"father begin read,message:"<<std::endl;
        ssize_t n = read(rfd,inbuffer,sizeof(inbuffer) - 1);//读取消息
        if(n > 0)
        {
            inbuffer[n] = 0;//语言限制,在字符串最后加\0
            std::cout<<inbuffer<<std::endl;//打印消息
        }
    }
}
int main()
{
    int pipefd[2];
    int n = pipe(pipefd);//管道创建成功,返回0
    if(n != 0)//管道创建失败
    {
        std::cerr<<"errno "<<errno<<"cerrstring: "<<strerror(errno)<<std::endl;
    }
    std::cout<<"读端->pipefd[0]"<<pipefd[0]<<"写端->pipefd[1]"<<pipefd[1]<<std::endl;
    sleep(1);
    pid_t id = fork();//创建子进程
    if(id == 0)
    {
        //子进程进行写入
        std::cout<<"子进程关闭不需要的fd了,准备写消息了"<<std::endl;
        close(pipefd[0]);//关闭读端
        SubProcessWrite(pipefd[1]);//子进程写
        close(pipefd[1]);//任务完成关闭写端
        exit(0);
    }
    //父进程
    std::cout<<"父进程关闭不需要的fd了,准备读消息了"<<std::endl;
    close(pipefd[1]);//关闭写端
    FatherProcessReader(pipefd[0]);//父进程读
    close(pipefd[0]);//任务完成关闭读端
    pid_t rid = waitpid(id,NULL,0);//父进程等待子进程,并回收
    return 0;
}

来看看运行结果:

 管道的4种情况:

1.写端进程不写,读端进程一直读,那么此时会因为管道里面没有数据可读,对应的读端进程会被挂起,直到管道里面有数据后,读端进程才会被唤醒。


2.读端进程不读,写端进程一直写,那么当管道被写满后,对应的写端进程会被挂起,直到管道当中的数据被读端进程读取后,写端进程才会被唤醒。


3.写端进程将数据写完后将写端关闭,那么读端进程将管道当中的数据读完后,就会继续执行该进程之后的代码逻辑,而不会被挂起。


4.读端进程将读端关闭,而写端进程还在一直向管道写入数据,那么操作系统会将写端进程杀掉。

管道的大小:

管道是有容量的,当管道被写满了,写端将会阻塞或者失败,查询管道大小的方法有如下:

ulimit -a指令,查看当前资源限制。

从上图可以算出管道的大小为512*8 = 4096字节。 

命名管道:

刚才介绍的匿名管道,只可用于父子进程间通信,如果两个毫不相干的进程要实现通信该怎么办呢?接下来就需要介绍一下命名管道了。

mkfifo函数:

mkfifo函数用于创建一个命名管道。

mkfifo的第一个参数表示要创建的命令管道文件,如果不带路径默认再当前文件夹下。

mkfifo的第二个参数表示管道的文件权限。

例如文件权限设置为0666,则理论创建的管道权限为

 但实际文件权限还会受文件默认掩码umask影响,默认的umask是0002,我们实际的文件权限会先0666&(~umask),所以实际管道权限为0664:

mkfifo的返回值: 

管道创建成功返回0。

创建管道失败返回-1,错误码被设置。 

用命名管道实现serve&client通信

serve管理管道负责创建,销毁和读取消息,client负责往管道中写入消息:

serve.cc:

#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <cerrno>
#include <cstring>

const std::string comm_name = "./myfifo";

int main()
{
    //服务端创建命名管道
    int res = mkfifo(comm_name.c_str(),0666);
    if(res != 0)//创建失败
    {
        perror("mkfifo");
    }

    //serve端打开管道
    int fd = open(comm_name.c_str(),O_RDONLY);
    if(fd < 0)
    {
        std::cout<<"open file"<<errno<<std::endl;
    }

    //serve接受消息并打印
    char buffer[1024];
    while(true)
    {
        std::cout<<"server begin read:"<<std::endl;
        ssize_t n = read(fd,buffer,sizeof(buffer)-1);
        if(n > 0)//读取成功
        {
            buffer[n] = 0;
            std::cout<<buffer<<std::endl;
        }
        else if( n == 0)
        {
            std::cout<<"read done"<<std::endl;
            break;
        }
        else
        {
            std::cout<<"read fail"<<errno<<std::endl;
            break;
        }
    }
    int n = unlink(comm_name.c_str());
    if(n != 0)
    {
        perror("unlink");
    }
    return 0;
}

client.cc:

#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <cerrno>
#include <cstring>

const std::string comm_name = "./myfifo";

int main()
{
    //client以写打开管道
    int fd = open(comm_name.c_str(),O_WRONLY);
    if(fd < 0)
    {
        std::cout<<"open fail"<<errno<<std::endl;
    }
    int cent = 100;
    sleep(5);
    while(cent--)
    {
        sleep(1);
        std::cout<<"client begin write"<<std::endl;
        //消息拼接
        std::string message = "i sent a message ,cnet: ";
        message += std::to_string(cent);
        //写入消息
        ssize_t n = write(fd,message.c_str(),sizeof(message));
    }
    return 0;
}

来看看运行结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1794091.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

EMC整改学习-笔记

EMC整改学习-笔记 来自赛盛技术的笔记 如果我拿到一个产品超标的一个频谱图的话&#xff0c;首先我们可以对比做一个分析。来确定你干扰源的一个分类和定义是哪些。是你这个产品类型&#xff0c;什么样的电路对应什么样的一个。从我们的一个大量的一个测试数据的经验来看&…

java mybatis处理大数据量,开启和配置二级缓存,及注意事项,已解决

注意事项&#xff1a; 尽量避免使用下面方式写sql否则会降低服务器性能&#xff1a; mybatis二级缓存开启后&#xff0c;避免使用事务注解&#xff08;加上事务注解后二级缓存数据会导致两次访问不一致问题&#xff09;&#xff1a; 3. 返回的对象实体类&#xff0c;要实现Se…

【启明智显彩屏应用】Model3A 7寸触摸屏在真空包装机上的应用解决方案

一、项目背景与需求 随着工业自动化水平的提升&#xff0c;对真空包装机的操作界面和控制精度要求也越来越高。为满足这一需求&#xff0c;我们提出了基于Model3A工业级HMI&#xff08;人机界面&#xff09;芯片方案的7寸触摸屏解决方案&#xff0c;旨在提高真空包装机的操作便…

RabbitMQ一、RabbitMQ的介绍与安装(docker)

一、RabbitMQ相关名词解释 MQ MQ全称Message Queue&#xff08;消息队列&#xff09;&#xff0c;是在消息的传输过程中保存消息的容器。 多用于系统之间的异步通信。 常见的两种通信方式&#xff1a; 同步通信&#xff1a;同步通信相当于两个人当面对话&#xff0c;你一言我…

SD321BF 低功耗单运算放大器芯片IC

一般说明 SD321为低功耗系统带来性能和经济性。具有高单位增益频率和保证0.4V/在此情况下&#xff0c;静态电流仅为430μa/aef(5V)。输入通用模式范围包括地面&#xff0c;因此该设备能够在单电源应用和双电源应用中工作。它还能够舒适地驱动大容量负载。 SD32…

【环境栏Composer】Composer常见问题(持续更新)

1、执行composer install提示当前目录中没有 composer.lock 文件时 No composer.lock file present. Updating dependencies to latest instead of installing from lock file. See https://getcomposer.org/install for more information. Composer 在执行 install 命令时会…

单投币的充电桩如何加装一个扫码模块

充电桩需要投币才能充电&#xff0c;可是现在的人们很少有带硬币的习惯&#xff0c;扫码成为了一个常规的手段。我们也会发现有的充电桩无法扫码&#xff0c;或者说扫码无效&#xff0c;那是因为充电桩没有安装扫码模块&#xff0c;那么充电桩该如何加装扫码模块。 首先将充电桩…

搭建智慧互联网医院系统教学:源码解析与在线问诊APP开发

本篇文章&#xff0c;小编将以“源码解析与在线问诊APP开发”为切入点&#xff0c;详细介绍搭建智慧互联网医院系统的过程。 一、智慧互联网医院系统的架构设计 系统架构概述 -前端 -后端 -数据库 功能模块划分 -用户 -预约 -挂号 -问诊、 -病历 -管理 -药品 -配送…

家长必看:学生如何正确使用台灯?精选适合学生写作业的台灯

中国目前面临着严重的近视问题&#xff0c;各学段学生的近视率普遍偏高&#xff0c;且高度近视的占比也不容忽视&#xff0c;儿童近视的问题已经成为了一个不容忽视的社会问题。为了让孩子在学习的过程中拥有更好的视力保护&#xff0c;不少家长会选择购买性价比高、健康护眼的…

深入解析:海外短剧推广平台的流媒体传输技术挑战与应对策略

在海外短剧推广平台的建设和运营过程中&#xff0c;流媒体传输技术扮演着至关重要的角色。然而&#xff0c;由于网络环境的复杂性和多样性&#xff0c;流媒体传输技术面临着诸多挑战。本文将深入解析这些挑战&#xff0c;并提出相应的应对策略。 一、流媒体传输技术的挑战 带…

令人瞠目结舌的8个ChatGPT-4o提示词

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

【关于傅里叶变换的一系列问题】

1. 为什么频率分辨率是 f s N \frac{f_s}{N} Nfs​​&#xff1f; 因为采样率 f s {f_s} fs​ 决定了最大频率范围&#xff08;奈奎斯特频率&#xff09;。时域信号长度 &#x1d441;决定了频域中的离散点数。DFT对长度为 &#x1d441;的时域信号进行变换&#xff0c;得到…

LVGL的移植

对这个源文件进行一定的裁剪&#xff0c;其余文件保留&#xff1a; 之后将examples中得文件进行裁剪&#xff0c;只保留输入输出设备文件porting 流程&#xff1a; 1&#xff0c;确定输入输出的设备 2&#xff0c;确定所需的可选功能 3&#xff0c;准备LVGL库、历程 4&#xf…

OSG天空图代码

osgEarth // 创建天空选项osgEarth::Util::SkyOptions skyOptions;// 设置天空的坐标系统&#xff08;可选&#xff09;skyOptions.coordinateSystem() osgEarth::Util::SkyOptions::COORDSYS_ECEF;// 设置一天中的小时数&#xff08;可选&#xff09;skyOptions.hours() 12.…

Vulnhub-DC-3

joomla3.7.0的提权 信息收集 靶机IP:192.168.20.136 kaliIP:192.168.20.128 网络有问题的可以看下搭建Vulnhub靶机网络问题(获取不到IP) 首先nmap扫端口和版本&#xff0c;dirsearch跑下目录&#xff0c;wappalyzer也可以用下 发现服务器用的ubuntu&#xff0c;JoomlaCMS…

实验9 浮动静态路由配置

--名称-- 一、 原理描述二、 实验目的三、 实验内容四、 实验配置五、 实验步骤 一、 原理描述 浮动静态路由也是一种特殊的静态路由&#xff0c;主要考虑链路冗余。浮动静态路由通过配置一条比主路由优先级低的静态路由&#xff0c;用于保证在主路由失效的情况下&#xff0c;…

RabbitMQ小结

MQ分类 Acitvemq kafka 优点&#xff1a;性能好&#xff0c;吞吐量高百万级&#xff0c;分布式&#xff0c;消息有序 缺点&#xff1a;单机超过64分区&#xff0c;cpu会飙高&#xff0c;消费失败不支持重试 &#xff0c; Rocket 阿里的mq产品 优点&#xff1a;单机吞吐量也…

PHP质量工具系列之php-depend

php-depend是一个开源的静态代码分析工具&#xff0c;它的主要功能包括&#xff1a; 代码质量分析 复杂度度量&#xff1a;计算类、方法和函数的Cyclomatic Complexity&#xff08;循环复杂度&#xff09;&#xff0c;帮助识别潜在的复杂代码段。 耦合度度量&#xff1a;分析类…

pytorch笔记:自动混合精度(AMP)

1 理论部分 1.1 FP16 VS FP32 FP32具有八个指数位和23个小数位&#xff0c;而FP16具有五个指数位和十个小数位Tensor内核支持混合精度数学&#xff0c;即输入为半精度&#xff08;FP16&#xff09;&#xff0c;输出为全精度&#xff08;FP32&#xff09; 1.1.1 使用FP16的优缺…

【AR开发-开源框架】使用Sceneform-EQR快速开发AR应用,当前接入了AREngine、ORB-SLAM,可快速地适配不同的安卓设备

Sceneform-EQR Sceneform 概览 Sceneform是一个3D框架&#xff0c;具有基于物理的渲染器&#xff0c;针对移动设备进行了优化&#xff0c;使您可以轻松构建增强现实应用程序&#xff0c;而无需OpenGL。 借助 Sceneform&#xff0c;您可以轻松地在 AR 应用和非 AR 应用中渲染…