【1++的Linux】之进程间通信

news2024/9/24 7:19:54

👍作者主页:进击的1++
🤩 专栏链接:【1++的Linux】

文章目录

  • 一,进程间通信的目的
  • 二,管道

一,进程间通信的目的

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

进程间通信的必要性:
若没有进程间通信,那么也就无法使用并发能力,无法实现进程间协同。传输数据,消息通知等。

进程是具有独立性的,虚拟地址空间和页表保证了其独立性,因此,进程间通信的成本是比较高的。
想要让两进程间能够通信,那么其必定要能够看到同一份 “内存” 。这份所谓的“内存”不能属于任何一个进程,它应该是共享的。

进程间通信的发展:

  1. 管道
  2. System V进程间通信
  3. POSIX进程间通信

管道:
匿名管道pipe
命名管道

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

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

二,管道

管道是Unix中最古老的进程间通信的形式。是Linux原生就能够提供的。其有一个入口,一个出口,是单向通信的,也可以说是一种特出的半双工通信。

管道的原理:
我们在上面提到,两进程之间能够进行通信,那么两进程之间就得都能看到同一份资源?那么怎么让两进程看到同一份资源呢?
在fork之后,创建出来的子进程会继承父进程的大多数内容,这其中就包括文件描述符表,那么文件对象会被拷贝给子进程吗?显然是不会的,这样做是没有意义的。
我们在创建子进程之前分别以读写方式打开同一个文件,子进程继承之后,其也能够这个文件的文件描述符,有了文件描述符,我们是不是就能够访问这个文件了!!!我们让父进程进行写,那么就关闭其读的那个文件描述符,让子进程读,那么就关闭其写的那个文件描述符。这样,父子进程间就能够就行通信了。这样通信方式我们叫做匿名管道。
管道的本质是一种文件。

下面我们来简单的实现一个匿名管道:

使用pipe系统调用来创建匿名管道。

#include<iostream>
#include<fcntl.h>
#include<unistd.h>
#include<cassert>
#include<cstring>
#include<sys/types.h>
#include<sys/wait.h>
using namespace std;

int main()
{
    //创建匿名管道
    int pipefd[2];//0读--1写
    int n=pipe(pipefd);
    assert(n!=-1);
    cout<<"creat pipe success"<<endl;
    (void)n;
    //创建子进程
    pid_t pid=fork();
    assert(pid!=-1);
    if(pid==0)//子进程
    {
        //子进程负责读
        close(pipefd[1]);//关闭写端
        char buffer[1024];
        while(true)
        {
            //sleep(3);
            ssize_t n=read(pipefd[0],buffer,sizeof(buffer)-1);
            assert(n!=-1);
           if(n>0)
           {
             buffer[n]='\0';
             cout<<"child get a message["<<getpid()<<"]"<<"father#"<<buffer<<endl;
           }
            if(n==0)
            {
                cout<<"write quite,me quite"<<endl;
                break;
            }
        }

        close(pipefd[0]);//可有可无
        exit(0);
    }

    //父进程写
    close(pipefd[0]);//关闭读
    const char* message="I am sending message";
    int count=0;
   while(true)
   {
     ssize_t n=write(pipefd[1],message,strlen(message));
     sleep(1);
     count++;
     if(count==5) break;
   }

   //读写完成,退出
  close(pipefd[1]);
  pid_t ret= waitpid(pid,nullptr,0);
  assert(ret>0);
  return 0;

    return 0;
}

运行结果:

写慢读快时
在这里插入图片描述
我们发现写慢读快时,读端不会继续写,而是停下来等待写入。

当我们让写快,读慢时(即读时休眠时间长一些)
在这里插入图片描述
一次会将管道中的所有数据都读出来。管道的大小是有限制的,当管道被写满时,便不会再写,而是等待读。

当把写端关掉,读端进程会直接退出。

当把读端关掉,OS会关掉写进程。

因此管道可以让进程间协同,提供了访问控制。
管道提供的是面向流式的通信服务,其生命周期随进程。

从管道读数据是一次性操作,数据一旦被读,它就从管道中被抛弃,释放空间以便写更多的数据。

在这里插入图片描述
站在内核的角度,管道的本质就是两个进程对同一个文件对象,一个进行写入,一个进行读取。
看待管道和看待文件一样,使用也是一样的,这也符合:Linux下一切皆文件的思想。

一个父进程可以和一个子进程通信,那么一个父进程能否和多个子进程分别通信?—可以的!
代码如下:

#pragma once
#include<iostream>
#include<functional>
#include<vector>
#include<string>
#include<unordered_map>
#include<unistd.h>
#include <utility> 
#include<cassert>
typedef std::function<void()> func;
std::vector<func> callbacks;
std::unordered_map<int,std::string> desc;
void readSQL()
{
    std::cout << "sub process[" << getpid() << " ] 执行访问数据库的任务\n" << std::endl;
}

void execule()
{
    std::cout << "sub process[" << getpid() << " ] 执行url解析\n" << std::endl;
}

void cal()
{
    std::cout << "sub process[" << getpid() << " ] 执行加密任务\n" << std::endl;
}

void save()
{
    std::cout << "sub process[" << getpid() << " ] 执行数据持久化任务\n" << std::endl;
}

void lod()
{ desc.insert({callbacks.size(), "readSQL: 读取数据库"});
    callbacks.push_back(readSQL);

    desc.insert({callbacks.size(), "execule: 进行url解析"});
    callbacks.push_back(execule);

    desc.insert({callbacks.size(), "cal: 进行加密计算"});
    callbacks.push_back(cal);

    desc.insert({callbacks.size(), "save: 进行数据的文件保存"});
    callbacks.push_back(save);

}

void showHandler()
{
    for(auto& e:desc)
    {
        std::cout<<e.first<<'\t'<<e.second<<std::endl;
    }
}

int Handersize()
{
    return callbacks.size();
}


#include<unistd.h>
#include<cstring>
#include<sys/types.h>
#include<sys/wait.h>
#include"task.hpp"
#define PROCESS_NUM 5

using namespace std;

int waitcommand(int waitfd,bool& quite)
{
    int command=0;
    ssize_t n=read(waitfd,&command,sizeof(command));
    if(n==0)
    {
        quite=true;
        return -1;
    }

    return command;

}

void SendCommand(int who,int fd,int command)
{
    ssize_t s=write(fd,&command,sizeof(command));
    std::cout<<"main process call"<<who<<"excule"<<desc[command]<<std::endl;
}

int main()
{
    lod();
    //pid  :  fd
    std::vector<std::pair<int,int>> slots;
    //多个子进程
    for(int i=0;i<PROCESS_NUM;i++)
    {
        //创建管道
        int pipefd[2];
        int n=pipe(pipefd);
        assert(n!=-1);
        //创建子进程
        pid_t pid=fork();
        assert(pid!=-1);
        if(pid==0)//子进程
        {
            //子进程读
            close(pipefd[1]);
            while(true)
            {
                //等命令
                bool quite=false;
                int command=waitcommand(pipefd[0],quite);//不写就等待
                if(command==-1)
                {
                    //std::cout<<"退出"<<std::endl;
                    break;
                }
                else if(command>=0&&command<=Handersize())
                {
                    callbacks[command]();
                }
                else
                {
                    std::cout<<"非法输入"<<std::endl;
                }
            }
            //退出
            close(pipefd[0]);
            std::cout<<"write quite,me quite"<<std::endl;
            exit(1);
            
        }

        //父进程写
        close(pipefd[0]);
        slots.push_back(std::make_pair(pid,pipefd[1]));
    }

    //随机派发命令
    srand((unsigned)time(nullptr));
    while(true)
    {
        int command=rand()%Handersize();//选择命令
        int choice=rand()%slots.size();//选择子进程
        //指派任务
        SendCommand(slots[choice].first,slots[choice].second,command);
        sleep(2);
    }

    //关闭所有写
    for(auto& e:slots)
    {
        close(e.second);
    }

    //回收所有子进程
     for(auto& e:slots)
    {
        waitpid(e.first,nullptr,0);
    }

    return 0;
}

在这里插入图片描述

命名管道:
命名管道与匿名管道的原理相同,都是通过让两个进程看到同一份资源,从而实现通信,但命名管道不再局限于父子进程之间,而是任意两个进程之间实现通信。
两进程看到相同的资源,是通过管道文件的路径从而实现的。
命名管道的本质也是一种文件,但不是普通的文件,普通的文件我们在读写时,会将内存数据刷新到磁盘中,但是我们的管道是不会的。因此其效率也是很高的。

管道文件的创建:

  1. mkfifo filename
  2. int mkfifo(const char *filename,mode_t mode);

下面是我们实现的命名管道的代码:

// 服务端接收消息

#include"comm.hpp"
#include"Log.hpp"

static void getmessage(int fd)
{
      char buffer[1024];
   while(true)
   {
        int n=read(fd,buffer,sizeof(buffer-1));
        assert(n!=-1);
        if(n>0)
        {
            buffer[n]='\0';
            std::cout<<"["<<getpid()<<"]"<<"client say: "<<buffer<<std::endl;
        }
        else if(n==0)
        {
            std::cout<<"["<<getpid()<<"]"<<"client quit,me quit"<<std::endl;
            break;
        }
   }
}

int main()
{
    //创建管道
    int n=mkfifo(ipc_path.c_str(),0666);
    if(n<0)
    {
        perror("mkfifo");
        exit(1);
    }
    log("管道创建成功",DEBUG)<<"step1"<<std::endl;

    //打开管道进行读
    int fd=open(ipc_path.c_str(),O_RDONLY);
    if(fd<0)
    {
        perror("open");
        exit(2);
    }
    log("打开管道成功",DEBUG)<<"step2"<<std::endl;
    for(int i=0;i<Process_Num;i++)
    {
        int pid=fork();
        assert(pid>=0);
        if(pid==0)
        {
            getmessage(fd);
            exit(1);
        }

    }
    for(int i=0;i<Process_Num;i++)
    {
        waitpid(-1,nullptr,0);
        std::cout<<"等待成功"<<std::endl;
    }
    close(fd);
    log("关闭管道成功",DEBUG)<<"step3"<<std::endl;
    unlink(ipc_path.c_str());
    log("删除管道成功",DEBUG)<<"step4"<<std::endl;

    return 0;
}

//客户端发送消息


#include<iostream>
#include"comm.hpp"
#include"Log.hpp"
#include<cstring>
int main()
{
    int fd=open(ipc_path.c_str(),O_WRONLY);
    if(fd<0)
    {
        perror("open");
        exit(3);
    }
    log("client 打开管道成功",DEBUG)<<"step5"<<std::endl;
    std::string buffer;
    while(true)
    {
        std::cout<<"client say:"<<std::endl;
        std::getline(std::cin,buffer);
        int n=write(fd,buffer.c_str(),buffer.size());

    }
    close(fd);
    return 0;

}
#pragma once
#include<iostream>
#include"comm.hpp"
#ifndef _LOG_H_
#define _LOG_H_

#define DEBUG 0
#define NOTICE 1
#define WARNING 2
#define ERROR 3

std::string mes[4]={
    "DEBUG","NOTICE","WARNING","ERROR"
};

std::ostream &log(std::string message,int level)
{
    std::cout<<"|"<<unsigned(time(nullptr))<<"|"<<mes[level]<<"|"<<message;
    return std::cout;
}


#endif
#pragma once
#include<iostream>
#include<sys/types.h>
#include<sys/stat.h>
#include<string>
#include<cassert>
#include<fcntl.h>
#include<unistd.h>
#include<sys/wait.h>
#define Process_Num 4

std::string ipc_path="./fifo.ipc";

一个普通的全局的静态函数与普通函数的区别是:用static修饰的函数,限定在本源码文件中,不能被本源码文件以外的代码文件调用。

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

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

相关文章

【Linux】权限完结

个人主页点击直达&#xff1a;小白不是程序媛 系列专栏&#xff1a;Linux被操作记 目录 前言 chown指令 chgrp指令 文件类型 file指令 目录的权限 粘滞位 umask指令 权限总结 前言 上篇文章我们说到对于一个文件所属者和所属组都是同一个人时&#xff0c;使用所属者身…

刀片式服务器介绍

大家都知道服务器分为机架式服务器、刀片式服务器、塔式服务器三类&#xff0c;今天小编就分别讲一讲这三种服务器&#xff0c;第二篇先来讲一讲刀片式服务器的介绍。 刀片式服务器定义&#xff1a;是一种高密度的服务器架构&#xff0c;通过多个独立服务器单元组成&#xff0c…

力扣每日一题74:搜索二维矩阵

给你一个满足下述两条属性的 m x n 整数矩阵&#xff1a; 每行中的整数从左到右按非严格递增顺序排列。每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target &#xff0c;如果 target 在矩阵中&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。…

C# 串口通信简单示例

C# 简单串口通信示例 串口通信示例代码 串口通信 C# 串口通信主要操作&#xff1a; 命名空间&#xff1a;using System.IO.Ports;获取端口&#xff1a;string[] ports System.IO.Ports.SerialPort.GetPortNames();设置端口名&#xff1a;serialPort1.PortName “COM1”; //…

【C++】继承 ⑫ ( 继承的二义性 | virtual 虚继承 )

文章目录 一、继承的二义性1、场景说明 - 继承的二义性2、继承中的二义性报错3、完整代码示例 二、virtual 虚继承1、虚继承引入2、虚继承语法3、代码示例 - 虚继承 一、继承的二义性 1、场景说明 - 继承的二义性 A 类 是 父类 , B 类 和 C 类 继承 A 类 , 是 子类 , D 类 多…

一文了解和使用nginx(附带图文)

前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&#xff1a;神的孩子都在歌唱 一文了解和使用nginx 一. nginx 简介1.1 什么是 nginx 和可以做什么事情1.2 正向代理1.3 反向代理1.4 负载均衡1.5 SSL 配置1.6 管理…

Galaxy生信云平台|Maftools高效地汇总、分析、注释和可视化肿瘤基因突变MAF文件...

2023-10-25&#xff0c;Galaxy中国镜像站 UseGalaxy.cn 平台新增 5 个工具。 MAF Tools Maftools-突变景观图: 绘制肿瘤基因突变景观图Maftools-突变汇总: 汇总MAF文件中的突变信息Maftools-共突变与互斥突变: 计算共突变和互斥突变Maftools-队列比较&#xff1a;比较两个队列之…

用VSCODE启动Java项目

下载插件 推荐下载插件 启动 在vscode中打开项目或将项目拖进vscode,等进度条加载完成即成启动项目

Redis快速上手篇(四)(Spring Cache,缓存配置)(注解方式)

Spring Cache 从3.1开始&#xff0c;Spring引入了对Cache的支持。其使用方法和原理都类似于Spring对事务管理的支持。Spring Cache是作用在方法上的 使用Spring Cache的时候我们要保证我们缓存的方法对于相同的方法参数要有相同的返回结果。 使用Spring Cache需要我们做两方面…

NSS [SWPUCTF 2022 新生赛]numgame

NSS [SWPUCTF 2022 新生赛]numgame 开题有一个数学表达式&#xff0c;试了一下不可能/-到正确的答案。 view-source:查看源码 解码之后是一个路由/NsScTf.php&#xff0c;访问一下得到了真正的源码。 访问一下/hint2.php call_user_func()&#xff1a;把第一个参数作为回调函数…

C/C++不及格学生 2020年9月电子学会青少年软件编程(C/C++)等级考试一级真题答案解析

目录 C/C不及格学生 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 C/C不及格学生 2020年9月 C/C编程等级考试一级编程题 一、题目要求 1、编程实现 给出一名学生的语文和数学成绩&#xff0c;判断他是…

代码随想录二刷 Day50

198.打家劫舍 这个题一开始由于给出来的例子陷入了思维误区&#xff0c;以为结果就是每隔一个取一个&#xff0c;其实有可能中间隔很多个。比如一下这个例子 下面这种写法不对。 class Solution { public:int rob(vector<int>& nums) {int odd_sum 0;int even_su…

10月最新H5自适应樱花导航网站源码SEO增强版

10月最新H5自适应樱花导航网源码SEO增强版。非常强大的导航网站亮点就是对SEO优化比较好。 开发时PHP版本&#xff1a;7.3开发时MySQL版本&#xff1a;5.7.26 懂前端和PHP技术想更改前端页面的可以看&#xff1a;网站的前端页面不好看&#xff0c;你可以查看index目录&#x…

“人类高质量数据”如何训练计算机视觉模型?

人类的视觉系统可以复制吗&#xff1f; 答案是肯定的。 计算机视觉 (Computer Vision) 技术的不断普及&#xff0c;让机器识别和处理图像就像人的大脑一样&#xff0c;且速度更快、更准确。 机器像人类一样去“思考” 计算机视觉 (Computer Vision) 是近年来人工智能增长最快…

Linux网络编程:IP协议

目录 一. IP协议的功能 二. IP协议报头 2.1 IP报头的格式 2.2 IP报头各部分含义 三. IP报文的分片问题 3.1 什么是分片 3.2 分片的原理 3.3 合并报文 四. 网段划分 4.1 网络号和主机号 4.2 网络号和主机号的划分策略 4.3 特殊的IP地址 4.4 IP地址数量不足问题 五.…

最新SQL注入漏洞修复建议

点击星标&#xff0c;即时接收最新推文 本文选自《web安全攻防渗透测试实战指南&#xff08;第2版&#xff09;》 点击图片五折购书 SQL注入漏洞修复建议 常用的SQL注入漏洞的修复方法有两种。 1&#xff0e;过滤危险字符 多数CMS都采用过滤危险字符的方式&#xff0c;例如&…

Kotlin中使用ViewBinding绑定控件并添加点击事件

文章目录 效果1、加入依赖2、与控件进行绑定在 Activity 中使用视图绑定 3、监听控件 效果 实现源码 class MainActivity : AppCompatActivity() {lateinit var binding:ActivityMainBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstan…

[GXYCTF2019]Ping Ping Ping 1

题目环境 给了一个ip参数 注意题目Ping Ping Ping 意思就是让我们进行Ping地址 随便输入一个地址Ping一下 URL?ip0 有回显结果&#xff0c;和上题类似 [ACTF2020 新生赛]Exec 1-CSDN博客 查看当前目录文件URL?ip0;ls&#xff08;这里使用堆叠注入查询&#xff09;直接给出了咱…

第10期 | GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练 Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大型语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以…

原来服务器这么有用-Docker安装

原来服务器这么有用-Docker安装 1. 前言 在此之前青阳通过各种方式介绍过自己通过服务器搭建的一些玩法&#xff0c;也写过一些教程&#xff0c;但是那些教程&#xff0c;现在回头来看&#xff0c;都是有些杂乱了&#xff0c;统一性不强。我就准备重新整理一下之前写的文章&a…