2024年C C++最新进程间通信 之 管道_进程管道通信,2024年最新面试学习

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

当用父子进程之间创建管道来通信时:

上述的管道特点、匿名管道的原理等。都可以使用下面的代码来测试(代码有超详细注释)

下面这段代码创建了一个简单的单向通信的管道

#include <iostream>
#include <unistd.h> // 操作系统调用接口, 只能 .h 结尾(规定的)
#include <cassert>  //   以 c 开头 + C语言头文件(不加 h) 就可以重新封装一套 C 语言头文件 #include<cstdio>  #include<cstring>
#include <sys/types.h>
#include <cstdio>
#include <cstring>
#include <sys/wait.h>
using namespace std;
#define MAX 1024

int main()
{
    int pipefd[2] = {0};

    // 第一步:建立管道
    int n = pipe(pipefd); // 管道创建成功    等于 0 创建成功,创建失败返回 -1
    assert(n == 0);       // 只在 debug 下存在   assert 用于意料之外的问题!
    (void)n;              // 假装使用一下, 防止 n 没有被使用而报警
    // cout << pipefd[0] << " " << pipefd[1] << endl; // 使用 pipe 函数后 pipe[0], pipe[1] 分别自动被设置为 3,4 (未被使用的两个最小的文件描述符)
    // pipefd[0] 读端  pipefd[1] 写端

    // 第二步:创建子进程
    pid_t id = fork();
    if (id < 0)
    {
        // 创建失败
        perror("fork");
        return 1;
    }

    // 第三步:父子关闭不需要的文件描述符,形成单向通信的信道
    // 子进程写入,父进程读取
    if (id == 0)
    {
        // 子进程
        close(pipefd[0]);
        int cnt = 10;
        // 只向管道写入
        while(cnt)
        {
            char message[MAX];
            snprintf(message, sizeof(message) - 1, "Hello father, I am child, pid: %d, cut: %d", getpid(), cnt);
            cnt--;
            write(pipefd[1], message, strlen(message));
            sleep(1);
        }
        // TODO
        exit(0);
    }
    // 父进程
    close(pipefd[1]); // 父进程关闭写端 
    char buffer[MAX];
    while(true)
    {
        ssize_t n = read(pipefd[0], buffer, sizeof(buffer) - 1); // 一会要给最后一个位置加 '\0',方便 C/C++ 使用(万一读满了, 就最多读 MAX - 1 个)
        // 面向字节流读取, 跟写的时候的格式无关, 读取的时候只跟字节数有关
        if(n > 0)
        {
            buffer[n] = 0; // 自己维护字符串(为 C++ 准备的)
            cout << getpid() << " :child say:" << buffer << " to me !" <<endl;
        }
        if(n == 0) break; // 说明写端关闭了(不是写端不写,而是关闭了)读端就会关闭
    }
    // 子进程退出后才能等待到
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if(rid == id)
    {
        // 等待成功
        cout << "wait success, child exit sig: " << (status &0x7F) << endl; // 退出信号 :status 的低 7 位
    }
    return 0;
}

使用管道技术设计一个简单的进程池

**进程池(Process Pool)**是一种并发编程的技术,它允许创建一组预先分配的子进程,这些子进程可以被重复地使用来执行任务。

通常情况下,每个进程都会拥有一个独立的地址空间和资源,这会导致进程的创建和销毁需要耗费大量的时间和资源。而进程池技术通过预先创建一组子进程,这些子进程会在一个池中等待任务分配。当有任务需要执行时,只需要将任务交给其中一个空闲的子进程即可,不需要重复地创建和销毁进程。

进程池通常由一个主进程(也称为管理进程)和一组子进程组成。主进程负责管理子进程的创建、销毁和任务分配,而子进程则负责执行实际的任务。

进程池的主要优势:提高效率、简化编程、提高可扩展性进程池主要适用于CPU密集型的任务,即任务主要涉及到计算和数据处理。

#include <iostream>
#include <cassert>
#include <unistd.h>
#include <functional>
#include <ctime>
#include <vector>
#include <string>
#include <sys/types.h>
#include <sys/wait.h>
typedef std::function<void()> task_t;
void Download()
{std::cout << "我是一个下载任务" << std::endl;}
void PrintLog()
{std::cout << "我是一个打印日志的任务" << std::endl;}
void PushVideoStream()
{std::cout << "这是一个推送视频流的任务" << std::endl;}
// void ProcessExit()
// {exit(0);}

class Init
{
public:
    // 任务码
    const static int g_download_code = 0;
    const static int g_printlog_code = 1;
    const static int g_pushVideostream_code = 2;
    // 任务集合
    std::vector<task_t> tasks;
public:
    Init()
    {
        tasks.push_back(Download);
        tasks.push_back(PrintLog);
        tasks.push_back(PushVideoStream);
        srand(time(nullptr) ^ getpid());
    }
    bool CheckSafe(int code)
    {
        if(code >= 0 && code < tasks.size()) return true;
        else return false;
    }
    void RunTask(int code)
    {
        return tasks[code]();
    }
    int SelectTask()
    {
        int code = rand() % tasks.size();
        return code;
    }
    std::string ToDesc(int code) //将任务码转化为任务字符传串
    {
        switch (code)
        {
            case g_download_code:
                return "g_download_code";
            case g_printlog_code:
                return "g_printlog_code";
            case g_pushVideostream_code:
                return "g_pushVideostream_code";
            default:
                return "Unknow";
        }
    }
};

Init init; // 定义对象
 
const int num = 5;
static int number = 1;
class channel // 先描述, 再组织
{
public:
    channel(int fd, pid_t id)
        : ctrlfd(fd),
          workerid(id)
    {
        name = "channel-" + std::to_string(number++);
    }

public:
    int ctrlfd;
    pid_t workerid;
    std::string name;
};

void Work()
{
    while (true)
    {
        int code = 0;
        ssize_t n = read(0, &code, sizeof(code)); // n 表示每次读到的字节个数
        if (n == 0) break;                               // 等于0 说明啥都没读到退出子进程并回收
        (void)n;
        if (!init.CheckSafe(code))
            continue; // code 不合法, 不在任务范围之内
        init.RunTask(code);
    }
}
void PrintDebug(const std::vector<channel> &c)
{
    for (const auto &channel : c)
    {
        std::cout << channel.ctrlfd << "," << channel.workerid << "," << channel.name << std::endl;
    }
}

void CreateChannels(std::vector<channel> *c)
{
    std::vector<int> old; // 用来保存父进程的写端文件描述符
    for (int i = 0; i < num; i++)
    {
        // 1. 先创建管道
        int pipefd[2];
        int n = pipe(pipefd);
        assert(n == 0);
        (void)n;

        // 2. 创建子进程
        pid_t id = fork();
        assert(id != -1);

        // 3. 构建单向信道
        if (id == 0) // child
        {
            if(!old.empty())
            {
                for(int i = 0; i < old.size(); i++)
                {
                    close(old[i]); // 关闭继承过来的父进程的文件描述符
                }
            }
            close(pipefd[1]);
            dup2(pipefd[0], 0); // 管道直接切换为从标准输入中读(重定向)
            Work();
            exit(0);
        }
        // father
        close(pipefd[0]);
        old.push_back(pipefd[1]);  //保存这次进程的写端, 方便下次的子进程关闭这些从父进程中继承过来的不需要要的端口
        c->push_back(channel(pipefd[1], id));
        // std::cout << id << std::endl;
    }
}
void SendCommand(std::vector<channel> &c, bool flag, int num = 0)
{


![img](https://img-blog.csdnimg.cn/img_convert/8ab7bdfae1e15fba1936a25981b58ff9.png)
![img](https://img-blog.csdnimg.cn/img_convert/f3d46a5eb612322c30928b6d9a5ca7b0.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

h2MUB-1715566861781)]
[外链图片转存中...(img-XDiBjGEQ-1715566861781)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值