【Linux后端服务器开发】管道设计

news2024/9/25 17:21:34

目录

一、管道通信

二、匿名管道

1. 匿名管道通信

2. 匿名管道设计

三、命名管道

comm.hpp

client.cc

serve.cc


一、管道通信

进程通信

数据传输:一个进程需要将它的数据发送给另一个进程

资源共享:多个进程之间共享同样的资源

通知事件:一个进程向另一个(一组)进程发送信息,通知它们发生了某种事件

进程控制:一个进程完全控制另一个进程的执行,如debug

通信本质

OS直接或间接给通信双方提供内存空间

通信的进程双方,能够读取到一份公共资源

管道文件是内存级文件

管道创建

 

管道读写端

  • 如果管道没有了数据,读端在读,默认会阻塞等待正在读取的进程
  • 管道是固定大小的空间,写端写满的时候,会阻塞等待读端读取
  • 写端关闭,读端在读,则读端读完管道数据也关闭
  • 读端关闭,写端在写,OS终止写端

int main() 
{
    int fds[2];
    int n = pipe(fds);
    assert(n == 0);

    cout << "fds[0]: " << fds[0] << endl;
    cout << "fds[1]: " << fds[1] << endl;

    return 0;
}

管道可用于父子、兄弟、祖孙进程之间通信

二、匿名管道

1. 匿名管道通信

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

int main() 
{
    // 1. 创建管道文件,打开读写端
    int fds[2];
    int n = pipe(fds);
    assert(n == 0);

    // 2. fork()
    pid_t id = fork();
    if (id == 0) 
    {
        //子进程通信代码
        close(fds[0]);  //关闭读端
        const char* s = "我是子进程,我正在给你发消息";
        int cnt = 0;
        while (true) 
        {
            char buffer[1024];
            snprintf(buffer, sizeof(buffer), "child->parent say: %s[%d][%d]", s, cnt++, getpid());
            write(fds[1], buffer, strlen(buffer));
            sleep(1);
        }
        exit(0);
    }

    //父进程通信代码
    close(fds[1]);  //关闭写端
    while (true) 
    {
        char buffer[1024];
        ssize_t s = read(fds[0], buffer, sizeof(buffer) - 1);
        if (s > 0) 
            buffer[s] = 0;
        cout << "Get Message# " << getpid() << buffer  << " | my pid: " << getpid() << endl;
        //父进程没有sleep
    }
    n = waitpid(id, nullptr, 0);
    assert(n == id);

    return 0;
}

2. 匿名管道设计

  • 将任务存入存入任务表vector中,将子进程存入信息存入subs容器中
  • 父进程随机将任务码发给5个子进程,子进程读取任务码完成任务(父进程为写端,子进程为读端),若子进程未读取任务,则进行阻塞等待
  • 通过随机数分配任务给随机子进程,让子进程负载均衡

代码设计流程:

  1. 建立任务表,建立子进程及和子进程通信的信道
  2. 父进程控制子进程,进行任务分配
  3. 回收子进程信息
#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <cassert>
#include <cstring>
#include <string>
#include <sys/types.h>
#include <sys/wait.h>
#include <vector>
using namespace std;

#define MakeSeed() srand((unsigned int)time(nullptr) ^ getpid())
#define PROCESS_NUM 5
#define TASK_NUM 10

typedef void(*func_t)();

void DownLoad() 
{
    cout << getpid() << " DownLoad Task" << endl;
    sleep(1);
}

void IOTask() 
{
    cout << getpid() << " IO Task" << endl;
    sleep(1);
}

void FlushTask() 
{
    cout << getpid() << " Flush Task" << endl;
}

void LoadTaskFunc(vector<func_t>& funcMap) 
{
    funcMap.push_back(DownLoad);
    funcMap.push_back(IOTask);
    funcMap.push_back(FlushTask);
}

/

class subEp 
{
public:
    subEp(pid_t subID, int writeFd)
        : _subID(subID), _writeFd(writeFd) 
    {
        char nameBuffer[1024];
        snprintf(nameBuffer, sizeof(nameBuffer), "process-%d[pid(%d)-fd(%d)]", num++, subID, _writeFd);
        _name = nameBuffer;
    }

    static int num;
    string _name;
    pid_t _subID;
    int _writeFd;
};

int subEp::num = 0;

// 读取
int RecvTask(int readFd) 
{
    int code = 0;
    ssize_t s = read(readFd, &code, sizeof(code));
    if (s == sizeof(int)) 
        return code;
    else if (s <= 0) 
        return -1;
    else 
        return 0;
}

void CreateSubProcess(vector<subEp>& subs, vector<func_t>& funcMap) 
{
    vector<int> deleteFd;
    for (int i = 0; i < PROCESS_NUM; ++i) 
    {
        int fds[2];
        int n = pipe(fds);
        assert(n == 0);
        (void) n;

        pid_t id = fork();
        if (id == 0) 
        {
            //建立一对一管道
            for (int i = 0; i < deleteFd.size(); ++i) 
                close(deleteFd[i]);
            //子进程处理任务
            close(fds[1]);
            while (true) 
            {
                // 1. 获取任务码,如果没收到任务码,则阻塞等待
                int commandCode = RecvTask(fds[0]);
                // 2. 执行任务
                if (commandCode >= 0 && commandCode < funcMap.size()) 
                    funcMap[commandCode]();
                else 
                    break;
            }
            exit(0);
        }
        close(fds[0]);
        subEp sub(id, fds[1]);
        subs.push_back(sub);
        deleteFd.push_back(fds[1]);
    }
}

void SendTask(const subEp& process, int taskNum) 
{
    cout << "send task num " << taskNum << " to " << process._name << endl;
    int n = write(process._writeFd, &taskNum, sizeof(taskNum));
    assert(n == sizeof(int));
    (void)n;
}

void LoadBlanceContrl(const vector<subEp>& subs, vector<func_t>& funcMap, int count) 
{
    int procNum = subs.size();
    int taskNum = funcMap.size();
    while (count--) 
    {
        int subIDx = rand() % procNum;
        int taskIDx = rand() % taskNum;
        SendTask(subs[subIDx], taskIDx);
        sleep(1);
    }
    for (int i = 0; i < procNum; ++i) 
        close(subs[i]._writeFd);
}

void WaitProcess(vector<subEp>& subs) 
{
    int procNum = subs.size();
    for (int i = 0; i < procNum; ++i) 
    {
        waitpid(subs[i]._subID, nullptr, 0);
        cout << "wait sub process success ... " << subs[i]._subID << endl;
    }
}

int main() 
{
    MakeSeed();
    // 1. 建立子进程并建立和子进程通信的信道
    //    [子进程id,wfd]
    vector<func_t> funcMap;
    LoadTaskFunc(funcMap);
    vector<subEp> subs;
    CreateSubProcess(subs, funcMap);

    // 2. 父进程控制子进程
    LoadBlanceContrl(subs, funcMap, TASK_NUM);

    // 3. 回收子进程信息
    WaitProcess(subs);

    return 0;
}

三、命名管道

让不同进程打开指定名称(路径+文件名)的同一个文件

comm.hpp

#pragma once

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

#define NAMED_PIPE "/root/test/pipe/named_pipe"

bool CreateFilo(const std::string& path) 
{
    umask(0);
    int n = mkfifo(path.c_str(), 0666);
    if (n == 0) 
    {
        return true;
    }
    else 
    {
        std::cout << "errno: " << errno << "err string: " << strerror(errno) << std::endl;
        return false;
    }
}

void RemoveFifo(const std::string& path) 
{
    int n = unlink(path.c_str());
    assert(n == 0);     //意料之中用assert判断,意料之外用 if else
    (void)n;
}

client.cc

#include "comm.hpp"

int main() 
{
    std::cout << "client begin" << std::endl;
    int wfd = open(NAMED_PIPE, O_WRONLY, 0666);
    std::cout << "client end" << std::endl;
    if (wfd < 0) 
        exit(1);

    //write
    char buffer[1024];
    while (true) 
    {
        std::cout << "please say# ";
        fgets(buffer, sizeof(buffer), stdin);
        if (strlen(buffer) > 0) 
            buffer[strlen(buffer) - 1] = 0;        
        ssize_t n = write(wfd, buffer, strlen(buffer));
        assert(n == strlen(buffer));
        (void)n;
    }

    close(wfd);
    return 0;
}

serve.cc

#include "comm.hpp"

int main() 
{
    bool r = CreateFilo(NAMED_PIPE);
    assert(r);
    (void)r;

    std::cout << "serve begin" << std::endl;
    int rfd = open(NAMED_PIPE, O_RDONLY);
    std::cout << "serve end" << std::endl;
    if (rfd < 0) 
        exit(1);

    //read
    char buffer[1024];
    while (true) 
    {
        ssize_t s = read(rfd, buffer, sizeof(buffer) - 1);
        if (s > 0) 
        {
            std::cout << "client->serve# " << buffer << std::endl;
        }
        else 
        {
            perror("read");
            exit(1);
        }
    }

    close(rfd);

    RemoveFifo(NAMED_PIPE);
    return 0;
}

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

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

相关文章

Matlab建模实战学习——(规划问题)

1.Matlab函数linprog&#xff08;&#xff09;的使用 1.1 Matlab基本描述 1.2 函数linprog&#xff08;&#xff09; 三种表达形式 [x,fval] linprog(c,A,b)[x,fval] linprog(c,A,b,Acq,bcq)[x,fval]linprog(c,A,b,Aeq,beq,lb,ub) 其中 x返回的是决策向量的取值&#xf…

K8S应用服务安全(最小特权 策略方案 资源限制 调用限制 沙箱)

应用服务安全 1 应用服务安全1.1 最小特权1.1.1 基础知识1.1.2 安全上下文1.1.3 资源实践1.1.4 特权基础1.1.5 特权实践1.1.5 PSP实践 1.2 策略方案1.2.1 OPA简介1.2.2 特权策略1.2.3 策略进阶 1.3 资源限制1.3.1 AppArmor简介1.3.2 基础知识1.3.2 应用实践 1.4 调用限制1.4.1 …

Git装机基础配置

Git在开发中可谓是必用的工具软件了&#xff0c;下面介绍下装机必须的几个配置 配置用户身份 git config –global user.name “Your Name” git config –global user.email “emailexample.com”// 例如&#xff1a; git config –global user.name “xiaowag” git config …

openpnp - 负压传感器DP102的设置

文章目录 openpnp - 负压传感器DP102的设置概述笔记上电后的画面查看模式进入设置模式输出1检测模式输出2检测模式输出1和输出2的常态(常开/常闭状态)设置动作响应时间输出动作颜色的设置压力单位的设置回到正常的运行模式输出1下限的设置输出1上限的设置输出2下限的设置输出2上…

有趣的命令——————用shell脚本实现三角形(直角三角形、等腰三角形)

直角三角形 vim zhijiao.sh 输入以下内容&#xff1a;#!/bin/bashread -p "几层的三角形:" nfor ((i1;i<$n;i)) dofor ((jn;j>i;j--))doecho -n " "donefor ((j1;j<i;j))doecho -n "*"doneecho done例&#xff1a; 测试&#xff1a; 等…

JDBC事务操作

1、 事务概述 事务的概念&#xff1a;逻辑上的一组SQL语句&#xff08;一个或者多个&#xff09;执行的时候要么全部执行成功&#xff0c;要么全部执行失败。 事务的案例&#xff1a;转账案例&#xff1a;一个账户中的余额减少&#xff0c;另一个账户中的余额增加。 更新数据…

Docker笔记 Docker架构与常用命令

大家好 我是积极向上的湘锅锅&#x1f4aa;&#x1f4aa;&#x1f4aa; 1. Docker 架构 首先Docker由三大部分组成 Clients:客户端 通过docker的客户端操作命令&#xff0c;然后命令发送到daemon&#xff08;守护进程&#xff09;里面去执行操作Hosts&#xff1a;主机 主机包…

Linux——内核概念

一、内核 什么是内核&#xff1f; 计算机是由各种外部硬件设备组成的&#xff0c;如内存、cpu、硬盘等。如果每个应用都要和这些硬件对接通信协议&#xff0c;就太麻烦了&#xff0c;所以这个工作就由内核来负责。内核作为软件连接硬件设备的桥梁&#xff0c;使应用开发者只需…

C++常用库函数 1.缓冲区操作函数

函数名&#xff1a;memchr 函数原型&#xff1a;void *memchr(const void *buf&#xff0c;int c&#xff0c;sizet count)&#xff1b; 参数&#xff1a;buf 缓冲区的指针&#xff1b;c 查找的字符&#xff1b;count 检查的字符个数。 所需头文件&#xff1a;<cstring&g…

Python算法中的时间复杂度与空间复杂度

大家好&#xff0c;当谈论算法性能时&#xff0c;经常提到两个关键的衡量标准&#xff1a;时间复杂度和空间复杂度。时间复杂度指的是随着输入规模的增长&#xff0c;执行一个算法需要的时间&#xff0c;而空间复杂度指的是一个算法在执行过程中所使用的内存量。本文将详细探讨…

机器学习-sigmoid函数和relu函数-个人解读

机器学习-sigmoid函数和relu函数-个人解读 今天博主来解读一下sigmoid函数和relu函数&#xff0c;我觉得很多同学可能都知道这两个函数是什么&#xff0c;他们干什么的&#xff0c;他们有什么用&#xff0c;但是呢&#xff1f;我想这两个常用的激活函数内在的本质&#xff0c;…

Ai数字人——为写实而生

在当今数字化时代&#xff0c;人工智能技术正成为推动各行各业发展的重要驱动力。企业需要创新的解决方案&#xff0c;以提高效率、降低成本、增强客户体验并应对市场竞争。Ai数字人作为数字化转型的关键组成部分&#xff0c;具备强大的潜力和多样化的应用场景。 根据市场研究…

dbeaver配置SQL server连接

一、需要java jdk环境&#xff0c;我用的比较新 新的&#xff0c;那坑可就来了&#xff0c;先记着 cmd中 java --version出现版本号代表安装成功 二、dbeaver新建连接SQL server 点击测试连接会自动下载驱动&#xff0c;这个时候重点报错就开始出现了 错误一&#xff1a;DBea…

【已解决】matrix contains invalid numeric entries,记录bug修改

文章目录 摘要原因解决办法图像分类网络AlexNetVGGNetGooLeNet系列ResNetDenseNetSwin TransformerMAECoAtNetConvNeXtV1、V2MobileNet系列MPViTVITSWAEfficientNet系列MOBILEVITEdgeViTsMixConvRepLKNetTransFGConvMAEMicroNetRepVGGMaxViTMAFormerGhostNet系列DEiT系列MetaFo…

CMOS图像传感器——三星ISOCELL HP2技术解析

在之前有写文章介绍三星在2022年技术日活动分享的包括图像传感器在内的各种系统相关技术的最新改进和未来愿景,可以回顾下看看。 三星ISOCELL——2022 改进与展望_沧海一升的博客-CSDN博客三星ISOCELL最新改进和未来发展趋势https://blog.csdn.net/qq_21842097/article/detai…

Stable Diffusion AI 绘画入门指南

Stable Diffusion AI 绘画入门指南 市面上用的最多的AI绘画工具是 Stable-Diffusion&#xff08;SD&#xff09; 和 Midjourney&#xff08;Mid&#xff09;&#xff0c;SD是在本地运行的服务&#xff0c;开源&#xff0c;门槛高&#xff0c;但自控性极高。而Mid作为在线的服务…

Redis——基础篇(包含redis在云服务上的docker化安装和连接以及常用命令)

初识Redis Redis为键值型数据库&#xff0c;数据以键值形式存储。没有表&#xff0c;没有约束。 认识NoSQL mysql就是典型的关系型数据库(SQL)。 目的都是数据的增删改查&#xff0c;但数据存储方式不一样。 关系型和非关系型在结构上有差异 关系型的结构一般定好后就很少修…

基于 FFmpeg 的跨平台视频播放器简明教程(六):使用 SDL 播放音频和视频

系列文章目录 基于 FFmpeg 的跨平台视频播放器简明教程&#xff08;一&#xff09;&#xff1a;FFMPEG Conan 环境集成基于 FFmpeg 的跨平台视频播放器简明教程&#xff08;二&#xff09;&#xff1a;基础知识和解封装&#xff08;demux&#xff09;基于 FFmpeg 的跨平台视频…

【unity小技巧】委托(Delegate)的基础使用和介绍

文章目录 一、前言1. 什么是委托&#xff1f;2. 使用委托的优点 二、举例说明1. 例12. 例2 三、案例四、泛型委托Action和Func1. Action委托2. Func委托 五、参考六、完结 一、前言 1. 什么是委托&#xff1f; 在Unity中&#xff0c;委托&#xff08;Delegate&#xff09;是一…

2FRE-10B-35-G24电比例调速阀控制器

2FRE-06B-20-G24、2FRE-10B-35-G24、2FRE-10B-50-G24、2FRE-16B-80-G24、2FRE-16B-115-G24、2FRE-16B-160-G24电比例调速阀&#xff0c;通过给定电信号&#xff0c;控制阀出口流量大小&#xff0c;并且采用压力补偿原理&#xff0c;保证输出流量大小不受负载压力影响维持恒定;该…