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

介绍

进程间通信的方式

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;
}

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

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

相关文章

最大异或对(trie树)

题目描述&#xff1a; 思路&#xff1a; 1、首先此题我们要知道异或的规则&#xff0c;这里不赘述了&#xff0c;可以百度 2、如果利用trie树去找到一个数字与其异或能得到最大值 比如二进制数&#xff1a;1010.....是一个很大的数 我们想要异或得到的值更大&#xff0c;就需…

AST解web控制流平坦化

此代码可以解决大部分 while if else 控制流平坦化原理&#xff1a; 先将 if 语句转为 switch 语句&#xff0c;再将 switch 分支合并&#xff0c;最后删除已合并的分支&#xff08;具体看代码&#xff09; 实现效果图 首先安装依赖&#xff1a; npm install babel/parser npm…

分布式文件存储与数据缓存(一)| FastDFS

目录 分布式文件系统FastDFS概述_简介FastDFS特性&#xff1a;分布式文件服务提供商 FastDFS概述_核心概念trackerstorageclientgroup FastDFS概述_上传机制内部机制如下 FastDFS概述_下载机制内部机制如下 FastDFS环境搭建_Linux下载安装gcc下载安装FastDFS下载安装FastDFS依赖…

c语言的字符串函数详解

文章目录 前言一、strlen求字符串长度的函数二、字符串拷贝函数strcpy三、链接或追加字符串函数strcat四、字符串比较函数strcmp五、长度受限制字符函数六、找字符串2在字符串1中第一次出现的位置函数strstr七、字符串切割函数strtok&#xff08;可以切割分隔符&#xff09;八、…

THM学习笔记—RootMe

nmap扫描&#xff0c;发现22端口和80端口打开 dirsearch扫描&#xff0c;注意到/panel和/uploads&#xff0c;在浏览器中打开 可以上传文件&#xff0c;尝试反弹shell 在尝试过程中发现网站不能上传.php文件&#xff0c;只需要将后缀更改为.php5之类即可 成功 查找文件&#x…

页面事件

下拉刷新事件 1. 什么是下拉刷新 下拉刷新是移动端的专有名词&#xff0c;指的是通过手指在屏幕上的下拉滑动操作&#xff0c;从而重新加载页面数据的行为。 2. 启用下拉刷新 启用下拉刷新有两种方式&#xff1a; ① 全局开启下拉刷新  在 app.json 的 window 节点中&…

Docker常用命令的使用及镜像的构建

1.docker的好处 在开发中可能会遇到一个问题&#xff0c;一个程序在自己电脑上能跑&#xff0c;但是换到服务器上就不行了。如果我们重新搭建环境&#xff0c;需要重新部署mysql,es,redis等组件很麻烦。有了docker之后&#xff0c;我们可以快速完成项目的部署。同时docker的隔…

MyBatis3源码深度解析(十二)MyBatis的核心组件(一)Configuration

文章目录 第四章 MyBatis的核心组件4.1 使用MyBatis操作数据库4.2 MyBatis核心组件4.3 Configuration组件4.3.1 属性4.3.2 设置4.3.3 类型别名4.3.3 类型处理器4.3.5 对象工厂4.3.6 插件4.3.7 配置环境4.3.8 映射器 第四章 MyBatis的核心组件 4.1 使用MyBatis操作数据库 在研…

《操作系统实践-基于Linux应用与内核编程》第10章-Linux综合应用

前言: 内容参考《操作系统实践-基于Linux应用与内核编程》一书的示例代码和教材内容&#xff0c;所做的读书笔记。本文记录再这里按照书中示例做一遍代码编程实践加深对操作系统的理解。 引用: 《操作系统实践-基于Linux应用与内核编程》 作者&#xff1a;房胜、李旭健、黄…

网络通信与网络协议

网络编程是指利用计算机网络实现程序之间通信的一种编程方式。在网络编程中&#xff0c;程序需要通过网络协议(如 TCP/IP)来进行通信&#xff0c;以实现不同计算机之间的数据传输和共享。在网络编程中&#xff0c;通常有三个基本要素 IP 地址:定位网络中某台计算机端口号port:定…

Pyqt5中,QGroupBox组件标题字样(标题和内容样式分开设置)相对于解除继承

Python代码示例&#xff1a; import sys from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QGroupBox, QLabelclass MyApp(QWidget):def __init__(self):super().__init__()# 创建一个 QVBoxLayout 实例layout QVBoxLayout()# 创建 QGroupBox 实例self.grou…

【中等】保研/考研408机试-二叉树相关

目录 一、基本二叉树 1.1结构 1.2前序遍历&#xff08;注意三种遍历中Visit所在的位置&#xff09; 1.2中序遍历 1.3后序遍历 二、真题实战 2.1KY11 二叉树遍历&#xff08;清华大学复试上机题&#xff09;【较难】 2.2KY212 二叉树遍历二叉树遍历&#xff08;华中科技大…

王道机试C++第8章递归与分治 Day35和蓝桥杯两道真题程序

第 8 章 递归与分治 递归是指&#xff1a;函数直接或间接调用自身的一种方法&#xff0c;通常可把一个复杂的大型问题层层转化为与原问题相似但规模较小的问题来求解。 递归策略只需少量的程序就可描述解题过程所需的多次重复计算&#xff0c;因此大大减少了程序的代码量。 8.…

OLED 菜单操作

本次介绍一款中景园带字库的OLED显示屏&#xff0c;并基于该模块描述一种菜单操作方法&#xff0c;能够极大的减少显示界面开发工作量。 使用的2.08寸OLED显示屏&#xff0c;字库芯片为GT30L32S4W&#xff0c;支持多种字号中英文。 官方提供了很完善的参考资料&#xff0c;包括…

结构体联合体枚举和位段

文章目录 结构体结构体类型的声明特殊的声明 结构的自引用结构体变量的定义和初始化结构体内存对齐为什么要内存对齐结构体传参结构体实现位段&#xff08;位段的填充&可移植性&#xff09;位段位段的内存分配空间如何开辟位段的跨平台问题位段的应用 枚举枚举类型的定义枚…

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:Column)

沿垂直方向布局的容器。 说明&#xff1a; 该组件从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 可以包含子组件。 接口 Column(value?: {space?: string | number}) 从API version 9开始&#xff0c;该接口…

vscode 生成树状图工具:project-tree

按下快捷键“CtrlShiftP”, 在弹框中输入 Project Tree&#xff0c;然后敲回车即会在根目录自动生成README.md&#xff08;如果之前没有的话&#xff09;。

pytorch 入门基础知识二(Pytorch 02)

一 微积分 1.1 导数和微分 微分就是求导&#xff1a; %matplotlib inline import numpy as np from matplotlib_inline import backend_inline from d2l import torch as d2l def f(x):return 3 * x ** 2 - 4 * x 定义&#xff1a; 然后求 f(x) 在 x 1 时的导数&#xff…

HarmonyOS NEXT应用开发—折叠屏音乐播放器方案

介绍 本示例介绍使用ArkUI中的容器组件FolderStack在折叠屏设备中实现音乐播放器场景。 效果图预览 使用说明 播放器预加载了歌曲&#xff0c;支持播放、暂停、重新播放&#xff0c;在折叠屏上&#xff0c;支持横屏悬停态下的组件自适应动态变更。 实现思路 采用MVVM模式进…

【Algorithms 4】算法(第4版)学习笔记 18 - 4.4 最短路径

文章目录 前言参考目录学习笔记0&#xff1a;引入介绍1&#xff1a;APIs1.1&#xff1a;API&#xff1a;加权有向边1.2&#xff1a;Java 实现&#xff1a;加权有向边1.3&#xff1a;API&#xff1a;加权有向图1.4&#xff1a;Java 实现&#xff1a;加权有向图1.5&#xff1a;AP…