Linux进程间通信之管道

进程间通信介绍:

进程间通信的概念:

进程间通信简称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;
}

来看看运行结果:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

12、SpringBoot 源码分析 - 自动配置深度分析五

SpringBoot 源码分析 - 自动配置深度分析五 refresh和自动配置大致流程OnClassCondition的createOutcomesResolver创建结果解析器StandardOutcomesResolver的resolveOutcomes解析结果StandardOutcomesResolver的getOutcomeClassNameFilter的MISSING判断是否没有 ThreadedOutcom…

具有 MOSFET 的电压到电流 (V-I) 转换器电路

设计说明 该单电源、低侧、V-I 转换器向可以连接到比运算放大器电源电压更高的电压的负载提供经过良好调节的电流。该 电路接受介于 0V 和 2V 之间的输入电压&#xff0c;将其转换为介于 0mA 和 100mA 之间的电流。通过将低侧电流检测电 阻 R3 上的压降反馈到运算放大器的反相…

Perfectly Clear WorkBench v4 解锁版安装教程 (图像修复增强工具)

前言 Perfectly Clear WorkBench 是一款图像修复工具&#xff0c;可以帮助用户对自己的图片素材进行修复&#xff0c;很多的照片因为拍摄问题&#xff0c;或者设备限制&#xff0c;会导致拍摄效果不好&#xff0c;使用这款软件可以进行一定程度的修复&#xff0c;当拍摄时亮度…

我找到了全网最低价买服务器的 bug !!!

拍断大腿 周五&#xff0c;放松一下&#xff0c;给大家分享下我最近的事儿&#xff0c;以及带大家薅个&#xff08;可能会有&#xff09;的羊毛。 上个月&#xff0c;家里买了 Apple TV&#xff08;可理解为苹果的电视盒子&#xff09;装了 infuse&#xff08;一个在电视盒子上…

机器学习扩展包MLXtend绘制分类模型决策边界

公众号&#xff1a;尤而小屋编辑&#xff1a;Peter作者&#xff1a;Peter 大家好&#xff0c;我是Peter~ 继续更新机器学习扩展包MLxtend的文章。本文介绍如何使用MLxtend来绘制与分类模型相关的决策边界decision_regions。 导入库 导入相关用于数据处理和建模的库&#xff…

狠狠打脸!国产AI大模型远比你想象的更强

龙争虎斗的大模型竞技场&#xff0c;这几天又有大事更新&#xff1a;6月7日&#xff0c;阿里云通义千问发布全球性能最强的开源模型Qwen2-72B。 包括72B在内&#xff0c;这次共开源五款模型&#xff0c;涵盖各个规格的参数&#xff0c;它们在同尺寸模型的测评中&#xff0c;都…

2024年安全现状报告

2024 年安全现状报告有些矛盾。尽管安全专业人员的道路困难重重&#xff0c;比如说严格的合规要求、不断升级的地缘政治紧张局势和更复杂的威胁环境&#xff0c;但整个行业还是在取得进展。 许多组织表示&#xff0c;与前几年相比&#xff0c;网络安全变得更容易管理。组织之间…

​【JS重点知识04】JS执行机制(重点面试题)

学前案例&#xff1a; console.log(111); setTimeout(function () {console.log(222); }, 1000) console.log(333); //输出结果&#xff1a;1111 333 222 console.log(111); setTimeout(function () {console.log(222); }, 0) console.log(333); //输出结果&#xff1a;111 33…

释放视频潜力:Topaz Video AI for mac/win 一款全新的视频增强与修复利器

在数字时代&#xff0c;视频已经成为我们记录生活、分享经历的重要方式。然而&#xff0c;有时候我们所拍摄的视频可能并不完美&#xff0c;可能存在模糊、噪点、抖动等问题。这时候&#xff0c;就需要一款强大的视频增强和修复工具来帮助我们提升视频质量&#xff0c;让它们更…

区块链简要介绍及运用的技术

一、区块链的由来 区块链概念最早是从比特币衍生出来的。 比特币&#xff08;Bitcoin&#xff09;诞生于2008年&#xff0c;是由一个名叫中本聪&#xff08;Satoshi Nakamoto&#xff09;的人首次提出&#xff0c;这个人非常神秘&#xff0c;至今没有他的任何准确信息。在提出…

ChatGPT-4o提示词的九大酷炫用法,你知道几个?

ChatGPT-4o提示词的九大酷炫用法&#xff0c;你知道几个&#xff1f;&#x1f680; 博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典…

优思学院|精益生产学习过程中如何提高自己的能力水平?

精益生产是一项实践多过理论的课题。 优思学院认为实践并不限于实际的工作&#xff0c;日常的思考同样重要&#xff0c;例如我们会要求学员在学习时不断思考各种事物&#xff0c;不限于自己的企业。例如当你去到一家餐厅&#xff0c;你能夠观察到什么浪费&#xff1f;你可否把…

2559. 统计范围内的元音字符串数(前缀和) o(n)时间复杂度

给你一个下标从 0 开始的字符串数组 words 以及一个二维整数数组 queries 。 每个查询 queries[i] [li, ri] 会要求我们统计在 words 中下标在 li 到 ri 范围内&#xff08;包含 这两个值&#xff09;并且以元音开头和结尾的字符串的数目。 返回一个整数数组&#xff0c;其中…

探秘Facebook:社交媒体的未来之路

Facebook&#xff0c;作为全球最大的社交媒体平台之一&#xff0c;一直处于数字社交革命的前沿。然而&#xff0c;随着科技和社会的不断发展&#xff0c;Facebook正面临着新的挑战和机遇。本文将探索Facebook的未来之路&#xff0c;揭示社交媒体的新趋势和发展方向。 1. 深度社…

nginx c++模块编译

不论是c还是c&#xff0c;nginx的第三方模块编写没什么太区别&#xff0c;但是提供给nginx调用的&#xff0c;必须是纯c的接口。 先说下为什么不能使用c编译nginx&#xff0c;nginx是纯c写的&#xff0c;而且c是兼容c的&#xff0c;但是用c(g)编译nginx的框架&#xff0c;就会出…

springboot编写简述01

项目结构 Users.java package com.sust.entity;import java.io.Serializable;public class Users implements Serializable {private String name;private String password;public String getName() {return name;}public void setName(String name) {this.name name;}publ…

【WEEK15】 【DAY3】Scheduled Tasks【English Version】

2024.6.5 Wednesday Following 【WEEK15】 【DAY2】【DAY3】Email Tasks【English Version】 Contents 17. Asynchronous, Scheduled, and Email Tasks17.3. Scheduled Tasks17.3.1. Two Annotations:17.3.2. Cron Expression17.3.3. Modify Springboot09TestApplication.java …

民主测评要做些什么?

民主测评&#xff0c;作为一种重要的民主管理工具&#xff0c;旨在通过广泛征求群众意见&#xff0c;对特定对象或事项进行客观、公正的评价。它不仅是推动民主参与、民主监督的重要手段&#xff0c;也是提升治理效能、促进社会和谐的有效途径。以下将详细介绍民主测评的主要过…

2.4 OpenCV随手简记(五)

一、图像翻转 第一个图像翻转&#xff0c;这个可是制作表情包的利器。 图像翻转在 OpenCV 中调用函数 flip() 实现&#xff0c;原函数如下&#xff1a; flip(src, flipCode, dstNone) src&#xff1a;原始图像。 flipCode&#xff1a;翻转方向&#xff0c; 如果 flipCode 为…

AI绘画如何打造高质量数据集?

遇到难题不要怕&#xff01;厚德提问大佬答&#xff01; 厚德提问大佬答11 你是否对AI绘画感兴趣却无从下手&#xff1f;是否有很多疑问却苦于没有大佬解答带你飞&#xff1f;从此刻开始这些问题都将迎刃而解&#xff01;你感兴趣的话题&#xff0c;厚德云替你问&#xff0c;你…