精彩管道不会梦到深沉蓝调

news2024/12/24 19:59:59

如果上天开了眼

请多给我点蓝调

多给我点沙锤

多给我点甲壳

让我吃鸡!

星元自动机,新的版本之神 

给宁磕一个

完蛋

你说这不是问题吗

我这篇文章从我写开始,到写完

炉石都换赛季了!!!!!

伙伴没了我心碎

#include<iostream>
#include<string>
#include<vector>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include"Task.hpp"

// void work(int rfd)
// {
//     while(true)
//     {
//         int command = 0;
//         int n = read(rfd, &command,sizeof(command)); 
//         if(n == sizeof(int))
//         {
//             std::cout << "pid is:" << getpid() << "handler task" << std::endl;
//             ExcuteTask(command);
//         }
//         else if(n == 0)
//         {
//             std::cout<<"Pipe closed"<<std::endl;
//             break;
//         }
//         else
//         {
//             perror("read");
//             break;
//         }
//     }
// }


//master

class Channel
{
public:
    Channel(int wfd, pid_t id, const std::string &name)
    :_wfd(wfd),_subprocessid(id),_name(name)
    {}
    int Getfd()const
    {
        return _wfd;
    }
    pid_t GetProcessId()const
    {
        return _subprocessid;
    }
    std::string GetName()const
    {
        return _name;
    }
    void CloseChannel()
    {
        close(_wfd);
    }
    void Wait()
    {
        pid_t rid = waitpid(_subprocessid,nullptr,0);
        if(rid > 0)
        {
            std::cout << "wait " <<rid << "success" << std::endl;
        }
    }
    ~Channel()
    {

    }
private:
    int _wfd;
    pid_t _subprocessid;
    std::string _name;
};

void CreateChannelAndSub(std::vector<Channel>* channels,int num1,task_t task)
{
    for(int i = 0; i < num1; i++)
    {
        //创建管道
        int pipefd[2] = {0};
        int n = pipe(pipefd);
        if(n < 0)
        {
            perror("pipe");
            exit(1);
        }

        //创建紫禁城
        pid_t id = fork();
        if(id < 0)
        {
            perror("fork");
            exit(1);
        }
        if(id == 0)
        {
            if(!channels->empty())
            {
                //第二次之后创建的管道
                for(auto &channel : *channels)
                {
                    channel.CloseChannel();
                }
            }
            //child
            close(pipefd[1]);
            dup2(pipefd[0],0);
            task();
            close(pipefd[0]);
            exit(0);
        }
       
        //父进程
        close(pipefd[0]);
        //构建名字
        std::string channel_name = "Channel  " + std::to_string(i);
        channels->push_back(Channel(pipefd[1],id,channel_name));
        //close(pipefd[1]);
    }
}

int NextChannel(int channelnum)
{
    static int next = 0;
    int channel = next;
    next++;
    next %= channelnum;
    return channel;
}

void SendTaskCommand(const Channel &channel,int taskcommand)
{
    size_t n = write(channel.Getfd(),&taskcommand,sizeof(taskcommand));
    if(n != sizeof(taskcommand))
    {
        perror("write");
    }
}

void CtrlProcessOnce(std::vector<Channel> &channels)
{
    sleep(1);
    //选择任务
    int taskcommand = Select();
    //选择信道和进程
    int channel_index = NextChannel(channels.size());
    //发送任务
    SendTaskCommand(channels[channel_index],taskcommand);

    std::cout << "taskcommand:" << taskcommand << "  channel:"\
    <<channels[channel_index].GetName() << "  sub process:"\
    << channels[channel_index].GetProcessId() << std::endl;
}

//通过channel来控制紫禁城
void CtrlProcess(std::vector<Channel> &channels,int times = -1)
{
    if(times > 0)
    {
       while(times--)
        {
            CtrlProcessOnce(channels);
        }
    }
    else
    {
        while(true)
        {
            CtrlProcessOnce(channels);
        }
    }
}

//回收管道和子进程
void CleanUpChannels(std::vector<Channel> &channels)
{
    // for(auto &channel : channels)
    // {
    //     channel.CloseChannel();
    // }

    for(auto &channel : channels)
    {
        channel.Wait();
    }
}

// ./processpool 5
int main(int argc,char* argv[])
{
    if(argc!=2)
    {
        std::cerr<<"Usage:"<<argv[0]<<"processnum"<<std::endl;
        return 1;
    }
    int num = std::stoi(argv[1]);
    LoadTask();

    std::vector<Channel> channels;
    //创建信道和子进程
    CreateChannelAndSub(&channels,num,work);

    //通过channel控制子进程
    CtrlProcess(channels,10);

    CleanUpChannels(channels);
   
    // for(auto &channel : channels)
    // {
    //     std::cout << " ---------------------- " <<std::endl;
    //     std::cout << channel.GetName() << std::endl;
    //     std::cout << channel.Getfd() << std::endl;
    //     std::cout << channel.GetProcessId() << std::endl;
    // }
    // sleep(100);
    return 0;
}

青春版Shell中添加管道实现

管道读写规则

当没有数据可读时

O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止

O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN

当管道满的时候

O_NONBLOCK disable: write调用阻塞,直到有进程读走数据

O_NONBLOCK enable:调用返回-1,errno值为EAGAIN

如果所有管道写端对应的文件描述符被关闭,则read返回0

如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE,进而可能导致write进程退出

当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性

当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性

管道特点 

只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信

一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道

管道提供流式服务

进程退出,管道释放,所以管道的生命周期随进程

内核会对管道操作进行同步与互斥 管道是半双工的,数据只能向一个方向流动

需要双方通信时,需要建立起两个管道

命名管道

原理

有进程、磁盘上有文件,进程要打开对应的文件描述符表struct files_struct

里面包含数组struct file* fd_array[ ]

struct file里面要有属性集合和操作集合

通过文件内核级的缓冲区向磁盘文件中写数据

另一个进程要打同一文件,要有对应的PCB和文件描述符表,有相对应的文件对象,新文件的属性集操作集文件内核缓冲区都差不多,没必要再创建一份,操作系统不会做浪费时间浪费空间的事

至此两份进程可以看到同一文件了

怎么保证两个毫不相关的进程打开的是同一文件呢?

每一个文件都有文件路径(这个文件路径具有唯一性)

这就是命名管道,依旧是内存级别的进行通信的方案

文件需要是特殊文件,文件打开后不会将数据刷新到磁盘,而是在内存级别进行文件通信

通过路径标识保证唯一性的叫命名管道

代码

这是个接口,可以制作一个FIFO

使用呢是这样用:

mkfifo myfifo

 可以这样进行读写:

如果想让左侧的终端不断向右侧写入则可以:

while :;do sleep 1;echo "hello named pipe"; done >> myfifo

 

那怎样让我们写的进程实行代码级别的通信呢?

还得是这个库函数

 这是一个unlink:

可以删除指定目录下的文件

 这是两个接口:创建一个管道、移除一个管道

namedPiped.hpp:

#pragma once

#include<iostream>
#include<sys/types.h>
#include<sys/stat.h>
#include<cerrno>
#include<cstdio>
#include<unistd.h>
#include<string>

const std::string comm_path = "./myfifo";

int CreateNamedPipe(const std::string &path)
{
    int res = mkfifo(path.c_str(),0666);
    if(res != 0)
    {
        perror("mkfifo");
    }
    return res;
}

int RemoveNamedPipe(const std::string &path)
{
    int res = unlink(path.c_str());
    if(res != 0)
    {
        perror("unlink");
    }
    return res;
}

 用cpp对代码封装:

#pragma once

#include<iostream>
#include<sys/types.h>
#include<sys/stat.h>
#include<cerrno>
#include<cstdio>
#include<unistd.h>
#include<string>

const std::string comm_path = "./myfifo";

class NamePiped
{
public:
    NamePiped(const std::string &path):_fifo_path(path)
    {
        int res = mkfifo(path.c_str(),0666);
        if(res != 0)
        {
            perror("mkfifo");
        }
    }
    ~NamePiped()
    {
        int res = unlink(_fifo_path.c_str());
        if(res != 0)
        {
            perror("unlink");
        }
    }
private:
    const std::string _fifo_path;
};

 相应的我们要对其进行身份的识别,是创建者才需要执行对应的操作:

#pragma once

#include<iostream>
#include<sys/types.h>
#include<sys/stat.h>
#include<cerrno>
#include<cstdio>
#include<unistd.h>
#include<string>

#define Creater 1
#define User 2

const std::string comm_path = "./myfifo";

class NamePiped
{
public:
    NamePiped(const std::string &path,int who):_fifo_path(path), _id(who)
    {
        if(_id == Creater)
        {
            int res = mkfifo(path.c_str(),0666);
            if(res != 0)
            {
              perror("mkfifo");
            }
        }
    }
    ~NamePiped()
    {
        if(_id == Creater)
        {
            int res = unlink(_fifo_path.c_str());
            if(res != 0)
            {
                perror("unlink");
            }
        }
    }
private:
    const std::string _fifo_path;
    int _id;
};

创建管道为了读写:

#pragma once

#include<iostream>
#include<sys/types.h>
#include<sys/stat.h>
#include<cerrno>
#include<cstdio>
#include<unistd.h>
#include<string>
#include<fcntl.h>

#define DefaultFd -1
#define Creater 1
#define User 2
#define Read O_RDONLY
#define Write O_WRONLY

const std::string comm_path = "./myfifo";

class NamePiped
{
private:
    //打开文件的模式
    bool OpenNamedPipe(int mode)
    {
        _fd = open(_fifo_path.c_str(),mode);
        if(_fd < 0)
        {
            return 0;
        }
        return true;
    }
public:
    NamePiped(const std::string &path,int who)
    :_fifo_path(path), _id(who),_fd(DefaultFd)
    {
        if(_id == Creater)
        {
            int res = mkfifo(path.c_str(),0666);
            if(res != 0)
            {
              perror("mkfifo");
            }
        }
    }
    bool OpenForRead()
    {
        return OpenNamedPipe(Read);
    }
    bool OpenForWrite()
    {
        return OpenNamedPipe(Write);
    }
    ~NamePiped()
    {
        if(_id == Creater)
        {
            int res = unlink(_fifo_path.c_str());
            if(res != 0)
            {
                perror("unlink");
            }
        }
        if(_fd != DefaultFd)
        {
            close(_fd);
        }
    }
private:
    const std::string _fifo_path;
    int _id;
    int _fd;
};

于是服务器端和客户端的调用方式也出来了

client.cc:

#include"namedPiped.hpp"

int main()
{
    NamePiped fifo(comm_path,User);
    fifo.OpenForWrite();
    return 0;
}

server.cc:

#include"namedPiped.hpp"

int main()
{
    NamePiped fifo(comm_path,Creater);
    fifo.OpenForRead();
    return 0;
}

还要有相应的读写管道的操作:

#pragma once

#include<iostream>
#include<sys/types.h>
#include<sys/stat.h>
#include<cerrno>
#include<cstdio>
#include<unistd.h>
#include<string>
#include<fcntl.h>

#define DefaultFd -1
#define Creater 1
#define User 2
#define Read O_RDONLY
#define Write O_WRONLY
#define BaseSize 4096

const std::string comm_path = "./myfifo";

class NamePiped
{
private:
    //打开文件的模式
    bool OpenNamedPipe(int mode)
    {
        _fd = open(_fifo_path.c_str(),mode);
        if(_fd < 0)
        {
            return 0;
        }
        return true;
    }
public:
    NamePiped(const std::string &path,int who)
    :_fifo_path(path), _id(who),_fd(DefaultFd)
    {
        if(_id == Creater)
        {
            int res = mkfifo(path.c_str(),0666);
            if(res != 0)
            {
              perror("mkfifo");
            }
        }
    }
    bool OpenForRead()
    {
        return OpenNamedPipe(Read);
    }
    bool OpenForWrite()
    {
        return OpenNamedPipe(Write);
    }
    
    //输出:const &:const std::string &XXX
    //输入:*      std::string *
    //输入输出:&   std::string &

    int ReadNamedPipe(std::string *out)
    {
        char buffer[BaseSize];
        int n = read(_fd,buffer,sizeof(buffer));
        if(n > 0)
        {
            //读取成功
            buffer[n] = 0;
            *out = buffer;
        }
        return n;
    }
    int WriteNamedPipe(const std::string &in)
    {
        return write(_fd,in.c_str(),in.size());
        
    }
    ~NamePiped()
    {
        if(_id == Creater)
        {
            int res = unlink(_fifo_path.c_str());
            if(res != 0)
            {
                perror("unlink");
            }
        }
        if(_fd != DefaultFd)
        {
            close(_fd);
        }
    }
private:
    const std::string _fifo_path;
    int _id;
    int _fd;
};

接下来就是实现服务器端和客户端进行通信:

client.cc:

#include"namedPiped.hpp"

int main()
{
    NamePiped fifo(comm_path,User);
    if(fifo.OpenForWrite())
    {
        std::cout << "Please Enteer -> ";
        std::string message;
        std::getline(std::cin,message);     //从标准输入中获取信息到message中
        fifo.WriteNamedPipe(message);
    }
    return 0;
}

 server.cc:

#include"namedPiped.hpp"

int main()
{
    NamePiped fifo(comm_path,Creater);
    if(fifo.OpenForRead())
    {
        while(true)
        {
            std::string message;
            int n = fifo.ReadNamedPipe(&message);
            if(n > 0)
            {
                std::cout << "Client Say " << message << std::endl;
            }
        }
    }
    return 0;
}

 

对于读端而言,如果我们打开文件,但是写端还没来,会阻塞在open调用中,直到对方打开

命名管道是通过文件路径让不同进程看到同一份资源的~

下篇说共享内存捏

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

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

相关文章

HTB-Redeemer(redis)

前言 各位师傅大家好&#xff0c;我是qmx_07&#xff0c;今天给大家讲解Redeemer这台机器&#xff0c;主要是对redis组件进行渗透&#xff0c;了解思路 渗透过程 更改一下 目录结构&#xff0c;先写 渗透过程&#xff0c;再写 题解 信息搜集 通过nmap扫描 发现开启了6379…

sklearn中的线性回归

多元线性回归 指的 是一个样本 有多个特征的 线性回归问题。 w 被统称为 模型的 参数&#xff0c;其中 w0 被称为截距&#xff08;intercept&#xff09;&#xff0c;w1~wn 被称为 回归系数&#xff08;regression coefficient&#xff09;。这个表达式和 yazb 是同样的…

CMake构建学习笔记4-libjpeg库的构建

libjpeg是一个广泛使用的开源库&#xff0c;用于处理JPEG&#xff08;Joint Photographic Experts Group&#xff09;图像格式的编码、解码、压缩和解压缩功能&#xff0c;是许多图像处理软件和库的基础。 libjpeg本身的构建没什么特别的&#xff0c;不过值得说道的是libjpeg存…

『功能项目』摄像机跟随角色【07】

我们打开上一篇06新输入系统项目&#xff0c; 本章要做的事情是摄像机跟随主角移动&#xff0c; 给主角增加一个Player标签方便主摄像机查找主角对象 在编辑场景调好角度&#xff0c;选择Main Camera对象按键盘Ctrl Shift F使运行场景与编辑场景相同 新建CameraCtrl脚本代码 …

Linux_NAT网络原理图,网络配置指令

目录 linux网络配置原理图 查看网络IP和网关 查看虚拟网络编辑器和修改IP地址 查看网关 ping测试主机之间网络连通性 linux网络配置原理图 ping指令的意思是连通上网&#xff0c;可以检测是否这个地址是否通。 比如我们在虚拟机里可以在火狐浏览器上打开百度。 使用ifco…

框架——动态SQL

目录 1.Mybatis动态SQL 2.MyBatis 中用于实现动态 SQL 的元素 3.查所有 4.If 元素 5.trim 元素 6.Choose 元素 7.set 元素 8.foreach 元素 根据传入id删除学生信息 根据传入列名查询学生相关信息 1.Mybatis动态SQL MyBatis 的一个强大的特性之一通常是它的动态 SQL 能…

CSRF简单介绍

欢迎交流 CSRF 条件&#xff1a; 需要请求伪造数据包无过滤防护&#xff0c;有过滤防护能绕过受害者需要触发&#xff08;诱惑&#xff09; 流程图 解决方案一&#xff1a; 检查Referer字段 解决方案二&#xff1a; CSRFToken 发货100CMS示例&#xff08;无过滤&#xff…

数据仓库系列4-什么是维度建模,它与关系型建模有什么区别

想象一下,你正在分析一家大型零售连锁店的销售数据。突然,你意识到传统的数据库模型无法有效地回答"去年黑色星期五当天,哪个地区的哪类产品销售额最高?"这样的复杂问题。这就是维度建模发挥作用的时候了 目录 引言:维度建模的魔力什么是维度建模?维度建模的定义维…

在我的博士科研生活中,SCI的英语写作一直是我的挑战。

在我的博士科研生活中&#xff0c;SCI的英语写作一直是我的挑战。尽管我不断地努力提高自己的语言水平&#xff0c;但每当我提交文章后&#xff0c;审稿人或编辑总是会指出语言表达的不足之处&#xff0c;让我深感苦恼。于是&#xff0c;我开始寻找专业的润色服务来帮助我提升文…

大模型之二十六-Diffusion model实例浅析

在2022年&#xff0c;midjourney、DALL-E以及Stable Diffusion三个文生图模型引爆了机器生产文生图领域&#xff0c;他们的模型能够在可控条件&#xff08;标签、文本描述&#xff09;下生成高分辨率、细节丰富的多样性图像&#xff0c;这在视觉上往往难以与真实图像区分。配套…

其它特殊库存

自有特殊库存 对于特殊库存&#xff0c;我们通常认为是由于所有权或存储地点与其它库存分开管理的物料库存。当过帐涉及特殊库存的货物移动时&#xff0c;除了移动类型外&#xff0c;必须&#xff08;如向特定供应商或客户或销售订单&#xff09;提供相关特殊库存标识以及更多…

3秒内搞定服务器端口扫描!用RustScan快速查看开放端口

文章目录 3秒内搞定服务器端口扫描&#xff01;用RustScan快速查看开放端口1. RustScan简介2. RustScan特点3. RustScan的基本使用3.1 创建alias别名3.2 基本用法3.3 常用参数说明3.4 示例4. 注意事项 最近开始公众号文章也开始同步更新了&#xff0c;对Java、大数据、人工智能…

游戏开发设计模式之命令模式

目录 命令模式的定义和工作原理 应用场景 实现方式 优点 缺点 结论 命令模式在游戏开发中的具体实现案例是什么&#xff1f; 如何在Unity3D中有效地实现和管理命令模式以提高游戏性能&#xff1f; 命令模式与其他设计模式&#xff08;如观察者模式、状态模式&#xff…

【C++ 面试 - 内存管理】每日 3 题(一)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏&…

JavaScript解构介绍

​在JavaScript中&#xff0c;解构赋值&#xff08;Destructuring Assignment&#xff09;是一种方便的语法&#xff0c;用于从数组或对象中提取值并将其赋给变量。解构使得代码更简洁、可读性更高&#xff0c;同时减少了重复的代码。 1. 数组解构 数组解构允许我们从数组中提…

2024最新、最热、最全Java容器【知识点】!!!

文章目录 Java容器种类详细说说他们都有哪些内容Collection :存储对象的集合 为什么HashSet和ArrayDeque不支持有序性操作Map&#xff1a;存储键值对的映射表 ArrayList和LinkedList的区别&#xff1f;ArrayList的增删一定比LinkedList的增删慢吗&#xff1f;native()方法是什么…

SOMEIP_ETS_064: String_UTF16FIXED_too_short

测试目的&#xff1a; 验证当设备&#xff08;DUT&#xff09;接收到一个小于64字节的UTF16FIXED字符串时&#xff0c;是否能够返回错误消息。 描述 本测试用例旨在检查DUT在接收到一个长度小于64字节的UTF16FIXED字符串的SOME/IP消息时&#xff0c;是否能够返回格式错误&am…

04-javascript 高阶(知识点)

目录 1.严格模式 1.1什么是严格模式 1.2开启严格模式 1.3严格模式中的变化 2.高阶函数 3.闭包 3.1 变量的作用域 3.2 什么是闭包 3.3 闭包的作用 3.4 闭包的案例 4.递归 4.1 什么是递归 4.2 利用递归求1~n的阶乘 5.赋值(地址传递)-浅拷贝-深拷贝 5.1 地址传递 5…

MATLAB中bicgstabl函数用法

目录 语法 说明 示例 线性系统的迭代解 使用指定了预条件子的 bicgstabl 提供初始估计值 使用函数句柄代替数值矩阵 bicgstabl函数的功能是求解线性系统 - 稳定双共轭梯度 (l) 法。 语法 x bicgstabl(A,b) x bicgstabl(A,b,tol) x bicgstabl(A,b,tol,maxit) x bic…

map容器的所有操作

1.基本概念 2.构造和赋值 注意map中存放的是pair对组&#xff0c;<key,value>&#xff0c;会根据key自动排序 3.大小和交换 4.插入和删除 插入的四种方式&#xff1a; 5.查找和统计 6.排序