6.【Linux】进程间通信(管道命名管道||简易进程池||简易客户端服务端通信)

news2024/11/24 13:51:06

介绍

进程间通信的方式

1.Linux原生支持的管道----匿名和命名管道
2.System V-----共享内存、消息队列、信号量
3.Posix------多线程、网路通信

进程间通信目的

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

管道

原理

管道是单向通信的,常用于有亲缘关系的父子间通信,父进程调用pipe打开管道文件,操作系统创建struct file结构体(存有inode等文件信息),父进程fd指向该文件,父进程fork出子进程,子进程也拷贝父进程的代码和fd,同时fd指向管道,分别关闭读端和写端,从而实现单向通信。
在这里插入图片描述

创建管道

在这里插入图片描述
调用成功返回0,失败返回-1。pipefd是输出型参数,pipefd【0】为3,pipefd【1】为4.

构建单向通信的读端(子进程关闭写端)

//2.create child process
    pid_t id=fork();
    assert(id!=-1);
    if(id==0)
    {
        //child process
        //3构建单向通行的信道pipe[0]:read,pipe[1]:write
        //3.1关闭子进程不需要的fd
        close(pipefd[1]);
        char buffer[1025];
        while(true)
        {
            ssize_t s=read(pipefd[0],buffer,sizeof(buffer)-1);
            if(s>0)
            {
                buffer[s]=0;
                cout<<"child get a message["<<getpid()<<"]father"<<buffer<<endl;
            }
        }


        exit(0);
    }

写端(父进程关闭读端)

	close(pipefd[0]);
    string message="我是父进程,我正在给你发消息";
    int count=0;
    char send_buffer[1024];
    while(true)
    {
        //3.2构建一个变化的字符串
        snprintf(send_buffer,sizeof(send_buffer),"%s:%d",message.c_str(),count++);
        //3.3写入
        write(pipefd[1],send_buffer,strlen(send_buffer));
        //3.4故意睡一会
        sleep(1);
    }

    pid_t ret=waitpid(id,nullptr,0);
    assert(ret<0);
    (void)ret;

    close(pipefd[1]);

管道特点

1.常用于亲缘关系的父子间通信
2.提供访问控制(例如管道无数据时读端就必须等数据写入)
3.管道本质是内核中的一块缓冲区,多个进程通过访问同一块缓冲区实现通信。
4. 管道提供的是面向流式的通信服务(面向字节流),需要定制协议来进行数据区分。
5.管道是基于文件的,文件的生命周期是随进程的,那么管道的生命周期也是随进程的。
6.管道是单向通信的,就是半双工通信的一种特殊情况,数据只能向一个方向流动。需要双方通信时,需要建立起两个管道。半双工通信就是要么在收数据,要么在发数据,不能同时在收数据和发数据(比如两个人在交流时,一个人在说,另一个人在听);而全双工通信是同时进行收数据和发数据(比如两个人吵架的时候,相互问候对方,一个人既在问候对方又在听对方的问候)。

实现一个简易进程池

process.cc

#include<iostream>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<assert.h>
#include<vector>
#include<cstdlib>
#include<time.h>
#include"Task.hpp"

#define PROCESS_NUM 5


using namespace std;


int waitCommand(int waitFd,bool &quit)
{
    uint32_t command=0;
    ssize_t s=read(waitFd,&command,sizeof(command));
    if(s==0)
    {
        quit=true;
        return -1;
    }
    assert(s==sizeof(uint32_t));
    return command;
}

//给哪一个进程通过什么文件描述符发送什么命令
void  sendAndWakeUp(pid_t who,int fd,uint32_t command)
{
    write(fd,&command,sizeof(command));
    cout<<"call process"<<who<<"execute"<<desc[command]<<"through"<<fd<<endl;
}


int main()
{
    Load();
    vector<pair<pid_t,int>> slots;
    //先创建多个进程
    for(int i=0;i<PROCESS_NUM;i++)
    {
        int pipefd[2]={0};
        int n=pipe(pipefd);
        assert(n==0);

        (void)n;

        pid_t id=fork();
        assert(id!=-1);

        if(id==0)
        {
            //child读取,关闭写端
            close(pipefd[1]);
            while(true)
            {
                bool quit=false;
                //pipefd[0]
                int command=waitCommand(pipefd[0],quit);//如果不发,则阻塞
                if(quit)    
                    break;
                if(command>=0&&command<handlerSize())
                {
                    callbacks[command]();
                }
                else
                {
                    cout<<"非法command"<<endl;
                }
            }
            exit(1);
        }
        close(pipefd[0]);
        slots.push_back(pair<pid_t,int>(id,pipefd[1]));

    }
    //开始任务
    srand((unsigned long)time(nullptr));
    while(true)
    {
        int select,command;
        cout<<"##############"<<endl;
        cout<<"1.show functions"<<endl;
        cout<<"2.send command"<<endl;
        cout<<"Please select";
        cin>>select;
        if(1==select)
        {
            showHandler();
        }
        else if(select==2)
        {
            cout<<"Enter Your Command:";
            cin>>command;
            //选择进程
            int choice_procID=rand()%slots.size();
            //布置任务给指定进程
            sendAndWakeUp(slots[choice_procID].first,slots[choice_procID].second,command);
        }

    }
    

    //关闭fd,所有的子进程都会退出
    for(const auto& slot:slots)
    {
        close(slot.second);
    }

    //回收所有的子进程信息
    for(const auto& slot: slots)
    {
        waitpid(slot.first,nullptr,0);
    }
    return 0;
}

task.hpp

#pragma once

#include<iostream>
#include<string>
#include<unistd.h>
#include<functional>
#include<vector>
#include<unordered_map>

typedef std::function<void()> func;

std::vector<func> callbacks;
std::unordered_map<int,std::string> desc;

void readMySQL()
{
    std::cout<<"process["<<getpid()<<"]执行访问数据库的任务"<<std::endl;
}

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

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

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

void Load()
{
    desc.insert({callbacks.size(),"readMySQL"});
    callbacks.push_back(readMySQL);

    desc.insert({callbacks.size(),"execul"});
    callbacks.push_back(execuleURL);
    
    desc.insert({callbacks.size(),"cal"});
    callbacks.push_back(cal);

    desc.insert({callbacks.size(),"save"});
    callbacks.push_back(save);

}

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

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

命名管道

引入

管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。
如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。命名管道是一种特殊类型的文件。

区别(打开方式,是否存在于文件系统,血缘)

匿名管道不属于文件系统,是一种特殊的文件,只存在于内存中,只能进行血缘关系间的通信。命名管道可用于无关联的进程间通信,mkfifo函数调用后需用open打开,因为它以FIFO文件的形式存在于文件系统中。

实现一个简易的客户端和服务端

//comm.hpp
#ifndef _COMM_H
#define _COMM_H


#include<iostream>
#include<string>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<cstdio>
#include<cstring>
#include"log.hpp"

using namespace std;

string ipcPath="./fifo.ipc";
const int MODE=0666;
const int SIZE=128;




#endif


//server.cc
#include"comm.hpp"

int main()
{
    //1.创建命名管道
    if(mkfifo(ipcPath.c_str(),MODE)<0)
    {
        perror("mkfifo");
        exit(1);
    }
    Log("创建管道文件成功",Debug)<<"step1"<<endl;

//2.打开命名管道
    int fd=open(ipcPath.c_str(),O_RDONLY);
    if(fd<0)
    {
        perror("open");
        exit(2);
    }
    Log("打开成功",Debug)<<"step2"<<endl;

    //3.读取数据
    char buf[SIZE];
    while(true)
    {
        memset(buf,'\0',sizeof(buf));
        ssize_t s=read(fd,buf,sizeof(buf)-1);//'\0'不读
        if(s>0)
        {
            cout<<"client say:"<<buf<<endl;
        }
        else if(s==0)
        {
            //EOF
            cerr<<"client quit!"<<endl;
            break;
        }
        else{
            //error
            perror("read");
            break;
        }
    }

    //4.关闭文件
    close(fd);
    unlink(ipcPath.c_str());
    Log("关闭成功",Debug)<<"step 3"<<endl;
    return 0;
}

//client.cc
#include"comm.hpp"
int main()
{
    //获取管道文件
    int fd=open(ipcPath.c_str(),O_WRONLY);
    if(fd<0)
    {
        perror("open");
        exit(1);
    }

    //ipc通信过程
    string buffer;
    while(true)
    {
        cout<<"please input:"<<endl;
        getline(cin,buffer);
        write(fd,buffer.c_str(),buffer.size());
    }
    close(fd);
    return 0;
}

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

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

相关文章

科研学习|论文解读——了解在线环境中的多数观点形成过程:Facebook的探索性方法(IPM, 2018)

论文标题 Understanding the majority opinion formation process in online environments: An exploratory approach to Facebook 摘要 在在线社区的社会互动过程中&#xff0c;多数观点经常被观察到&#xff0c;但很少有研究用实证数据来解决这一问题。为了确定一个合适的理论…

解决MySQL密码无法设置问题

引言 当我们第一次在Linux中安装好MySQL服务后&#xff0c;使用如下命令可以查看第一次进入MySQL的临时密码。 cat /var/log/mysqld.log | grep password但是我们如何修改它为自己想要的永久MySQL密码呢&#xff1f;在修改过程中出现的密码太短而无法设置的问题如何解决呢&am…

2024 新版 mysql 和 DBeaver 的安装教程,来啦!

mysql community 官网&#xff1a;https://dev.mysql.com/ DBeaver Community 官网&#xff1a;https://dbeaver.io/download/ 1. mysql 安装教程 进入官网 这里我选择的是社区版&#xff08;对于新手来说&#xff0c;学生是再好不过了&#xff0c;免费又开源&#xff09; 选择…

图解Transformer——注意力计算原理

文章目录 1、输入序列怎样传入注意力模块 2、进入注意力模块的矩阵的每一行&#xff0c;都是源序列中的一个词 3、每一行&#xff0c;都会经过一系列可学习的变换操作 4、如何得到注意力分数 5、Query、Key、Value的作用 6、点积&#xff1a;衡量向量之间的相似度 7、Transform…

如何使用机器学习构建自己的推荐系统?

一、说明 在广阔的电子商务领域&#xff0c;众多产品和服务都在争夺我们的注意力&#xff0c;推荐系统的作用变得至关重要。这些智能系统彻底改变了我们在线发现和接触产品的方式&#xff0c;使其成为现代电子商务平台成功的基石。 推荐系统&#xff0c;通常称为推荐引擎或简称…

The service already exists!怎么解决,Windows怎么安装/卸载服务?

问题描述 有时候,我们在Windows系统上安装服务时会遇到报错,The service already exists! 问题分析 这个报错说明此服务已经存在了,所以我们不能再次安装,但有时候我们明明是第一次安装,为什么也会报这个错误呢? 在Windows上注册服务通常需要使用命令行工具或者特定的…

如何降低node.js版本(nvm下载安装与使用)

一、问题场景 项目打包时提示以下错误 error js/chunk-f9fc6130.f14f870a.js from Terser Error: error:0308010C:digital envelope routines::unsupportedat new Hash (node:internal/crypto/hash:71:19)at Object.createHash (node:crypto:133:10) 这个错误提示表明在执行Ter…

大模型学习笔记(一):部署ChatGLM模型以及stable-diffusion模型

大模型学习笔记&#xff08;一&#xff09;&#xff1a;部署ChatGLM模型以及stable-diffusion模型 注册算力平台&#xff08;驱动云平台&#xff09;1.平台注册2.查看算力3.进入平台中心 部署ChatGLM3-6B模型1.创建项目2.配置环境设置镜像源、克隆项目修改requirements 3.修改w…

leetcode刷题日记之串联所有单词

题目描述 解题思路 一开始考虑的就是暴力破解&#xff0c;每次切片切words中字母的个数&#xff0c;然后根据每个词语的长度进行进一步的切片&#xff0c;将切出来的单词放入列表&#xff0c;然后每次对比一次&#xff0c;如果存在&#xff0c;就从原来的列表中&#xff0c;删…

AcWingP1101. 献给阿尔吉侬的花束-bfs(宽度优先搜索)

题目链接 搜宽&#xff1a;每次取出队头元素&#xff0c;然后将该队头元素扩展出的所有元素放到队尾 需要的数组&#xff1a; 判重数组&#xff1a;st[] 一般是入队时判重&#xff08;这样可以保证每一个点入队一次&#xff09;queue 队列 宽搜一般模板 queue <- 初始…

webots的安装和体验

刚知道webots是一个机器人仿真软件&#xff0c;好像离开硬件可以自己玩玩&#xff0c;而且有人形机器人的源代码&#xff0c;试试看吧。 Cyberbotics: Robotics simulation with Webotshttps://www.cyberbotics.com/ 官网下载&#xff0c;有windows版本&#xff0c;看上去好简…

java小型人事管理系统

开发工具&#xff1a; MyEclipseJdkTomcatSQLServer数据库 运行效果视频&#xff1a; https://pan.baidu.com/s/1hshFjiG 定制论文&#xff0c;联系下面的客服人员

微服务网关Spring Cloud Gateway有什么作用?

一、背景 现在的spring cloud生态中&#xff0c;spring cloud gateway是一个非常重要的组件。它是一款API网关服务&#xff0c;在微服务架构中扮演了关键角色&#xff0c;它为微服务架构中的请求提供了路由、转发和过滤的功能。此外&#xff0c;还提供了负载均衡、限流和鉴权的…

ARCGIS PRO SDK 图层唯一值渲染

一、如果【地块回退】为面, 按"DKMC"字段&#xff0c;在面中心配置符号Pushpin Await QueuedTask.Run(Sub()pFeaturelayer pmap.FindLayers("地块回退").First()-----------------唯一值渲染Dim Fields New List(Of String) From {"DKMC"} …

Unity中UGUI中的PSD导入工具的原理和作用

先说一下PSD导入工具的作用&#xff0c;比如在和美术同事合作开发一个背包UI业务系统时&#xff0c;美术做好效果图后&#xff0c;程序在UGUI中制作好界面&#xff0c;美术说这个图差了2像素&#xff0c;那个图位置不对差了1像素&#xff0c;另外一个图大小不对等等一系列零碎的…

【嵌入式DIY实例】-自动割草机器

自动割草机器 文章目录 自动割草机器1、割草机器介绍2、硬件准备3、功能设计4、硬件接线5、代码实现本文将介绍如何使用 Arduino 构建一个简易自动割草机机器人或割草机机器人。该机器人可以自动剪掉花园里多余的草。如果花园里有障碍物,它会自动改变方向。帮助以减少人力。 警…

AI - 支持向量机算法

&#x1f9e8;概念 支持向量机&#xff08;Support Vector Machine, SVM&#xff09;是一种强大的机器学习算法&#xff0c;主要用于解决二分类问题。 SVM的核心思想是找到一个超平面&#xff0c;这个超平面能够最好地将数据分为两类&#xff0c;即在保证分类准确的情况下&am…

LGB2028 反向输出一个三位数

&#xff08;LG是洛谷&#xff08;洛谷&#xff09;的意思&#xff09; 题目链接&#xff1a;B2028 题目描述 将一个三位数反向输出&#xff0c;例如输入 358&#xff0c;反向输出 853。 输入格式 一个三位数 n。 输出格式 反向输出 n。 输入输出样例 输入 #1 100 输…

MySQL语法分类 DQL(3)排序查询

为了更好的学习这里给出基本表数据用于查询操作 create table student (id int, name varchar(20), age int, sex varchar(5),address varchar(100),math int,english int );insert into student (id,name,age,sex,address,math,english) values (1,马云,55,男,杭州,66,78),…

CI/CD实战-gitlab代码仓库 2

gitlab安装部署 实验虚拟机最小需求&#xff1a;4g内存&#xff0c;4核cpu 下载源&#xff1a;Index of /gitlab-ce/yum/el7/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 安装依赖性&#xff1a; 安装软件&#xff1a; 修改依赖性配置文件 重新自动化配置 登录…