Linux进程间通信【匿名管道和命名管道】

简介: Linux进程间通信,包括匿名管道和命名管道的原理、操作及实现,两种管道实现进程池等丰富内容,详细讲解,干货满满!

Linux进程间通信【匿名管道和命名管道】

进程间通信,就是为了让两个不同进程间协作完成任务,通信的前提就是要构建两个进程之间的联系,构建联系的方法有很多种,本文先来谈谈管道通信

1. 进程间通信介绍

先来介绍一下进程间通信的相关概念

1.1 进程间通信目的

进程间通信的四个目的

  1. 数据传输:一个进程需要把自己的数据传输给另外一个进程
  2. 资源共享:多个进程之间共享同样的资源
  3. 通知事件:一个进程需要向另外一个或多个进程发送消息,通知它们发生某种事件
  4. 进程控制:有些进程需要完全控制另外一个进程的执行

1.2 进程间通信要求

进程间是有独立性的,不能违背这一原则,所以不能让一个进程直接访问另一个进程的数据

  • 要让两个不同的进程进行通信,前提条件就是:==先让两个进程看到同一份“资源”==,这也是进程间通信的本质,这里的“资源”就由操作系统直接或间接的提供

那么后续使用的任何进程间通信手段,无非就是要解决两个问题

  1. 想办法,先让不同的进程看到同一份“资源”
  2. 让一方写入,另一方读取,完成通信过程。至于通信目的与后续工作,要结合具体场景

1.3 进程间通信分类

管道:很古老的通信方式

  • 匿名管道
  • 命名管道

System V 标准:本地化进程通信方式

  • 消息队列
  • 共享内存
  • 信号量

POSIX标准:网络中进程通信方式

  • 消息队列
  • 共享内存
  • 信号量
  • 互斥量(互斥锁)
  • 条件变量
  • 读写锁

2. 匿名管道

管道是Unix中最古老的进程间通信的形式,我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”

在命令行中输入|`即可使用管道,我们利用管道来统计一下当前用户登录个数

who是一个命令,本质它是一个进程,是bash的子进程,wc同样是一个bash的子进程,通过管道的方式把who进程的数据也让wc进程看到

Linux下一切皆文件,管道也是文件,who进程一般都是从标准输出中获取的信息,wc进程一般都是从标准输入中获取信息的,将who进程的标准输出重定向到管道这个文件中,wc进程则将标准输入重定向到管道文件中,所以这样就可以实现两个进程看到同一份“资源”了

2.1 工作原理

借助上面的例子来讲解一下工作原理

  • 创建子进程,只会复制进程相关的数据结构对象,并不会拷贝父进程的文件对象。所以上述两个进程中文件描述符表中每个指针的指向的都是同一个文件对象
  • 这就是fork之后,父子进程会向同一个显示器打印数据的原因。这里OS提供的内存文件就是管道文件,这样就可以达成共享同一"资源"。管道文件不是放在磁盘中的,是一个内存文件,它只是支持单向通信(半双工)
  • 管道文件是确定数据流向的,关闭不需要的fd,也就是当who进程进行writewc进程进行read的时候,此时who进程就会关闭read对应的描述符(读端),wc进程就会关闭write对应的描述符(写端),这样就可以实现管道之间的单向关系了。这里的这个管道也就是匿名管道,不知道它对应的路径,也不知道对应的文件等等

2.2 pipe函数

#include <unistd.h>

int pipe(int pipefd[2]);

函数详解

  • pipe()创建一个管道,这是一个单向数据通道,可用于进程间通信。数组pipefd用于返回2引用管道两端的文件描述符。pipefd[0]表示管道的读端pipefd[1]表示管道的写端。写入管道写端的数据被内核缓冲,直到从管道的读端读取
  • 如果匿名管道创建成功,返回0,失败则返回-1,并适当地设置errno

参数记忆

  • pipefd[0]0就像嘴巴 ,嘴巴是用来读书的,所以对应读端
  • pipefd[1]1就像钢笔,钢笔是用来写字 ,所以对应写端

下面来个代码案例

#include <iostream>
#include <unistd.h>
#include <cerrno>
#include <cstring>
#include <cassert>
#include <cstdlib>
#include <string>
#include <stdio.h>
#include <sys/types.h>
using namespace std;

int main()
{
   
   
    //1. 创建匿名管道
    int pipefd[2] = {
   
   0};
    int ret = pipe(pipefd);
    if(ret == -1)
    {
   
   
        cout << "pipe failed, errno: " << errno << strerror(errno) << endl;
    }

    cout << "pipefd[0]:" << pipefd[0] << endl;  //fd:3 -> 读端 
    cout << "pipefd[1]:" << pipefd[1] << endl;  //fd:4 -> 写端

    //2. 创建子进程
    pid_t id = fork();
    assert(id != -1);
    if(id == 0) //子进程(写入)
    {
   
   
        close(pipefd[0]); //关闭读端

        //4. 开始通信
        int cnt = 5;
        char buffer_child[1024]; //缓冲区
        while(cnt--)
        {
   
   
            snprintf(buffer_child, sizeof(buffer_child), "我是子进程, 我的PID是: %d, cnt: %d", getpid(), cnt + 1);
            write(pipefd[1], buffer_child, strlen(buffer_child));
            sleep(1);
        }
        exit(0);    
    }

    //父进程(读取):
    //3. 关闭不需要的fd(父进程读取,子进程写入)
    close(pipefd[1]); //关闭写端
    char buffer_parent[1024];
    int num = 5;
    while(num--)
    {
   
   
        int n = read(pipefd[0], buffer_parent, sizeof(buffer_parent) - 1);
        if(n == -1)
        {
   
   
            cout << "read failed, errno: " << errno << strerror(errno) << endl;
        }
        buffer_parent[n] = '\0';
        cout << "我是父进程, 子进程给我传递的信息是:" << buffer_parent << endl;
    }
    return 0;
}

站在文件描述符的角度深入理解管道

站在内核的角度理解管道的本质

所以,看待管道,就如同看待文件一样!管道的使用和文件一致,迎合了“Linux一切皆文件思想”

3. 管道读写规则

管道读写规则

  • 当没有数据可读时
    • read调用阻塞,即进程暂停执行,一直等到有数据来到为止。
    • read调用返回-1errno值为EAGAIN
  • 当管道满的时候
    • write调用阻塞,直到有进程读走数据
    • 调用返回-1errno值为EAGAIN
  • 如果所有管道写端对应的文件描述符被关闭,则read返回0
  • 如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE,进而可能导致write进程退出
  • PIPE_BUF 为管道大小,Linux 中为 4096 字节
    • 当要写入的数据量不大于PIPE_BUF时,Linux将保证写入的原子性
    • 当要写入的数据量大于PIPE_BUF时,Linux将不再保证写入的原子性

4. 管道特点

管道的五个特点

  1. 管道是单向通信(半双工)
  2. 管道本质是文件,文件描述符声明周期是随进程的,管道的声明周期是随进程的
  3. 不仅父子进程可以通信,爷孙进程也可以通信,兄弟进程也可以通信,所以管道通信通常用来具有"血缘关系"的进程进行进程间通信。(pipe()打开管道并不清楚管道名字 --> 匿名管道)
  4. 写入的次数和读取的次数不是严格匹配的,可能一次写的东西分很多次读,可能多次写的东西一次读取,读写没有强相关
  5. 如果一个进程read端读取完管道所有数据,对方如果不发,那么只能等待;如果write端写满管道了,不能够写了;管道具有一定的协同能力,让读端和写端能够按照顺序通信(自带同步机制)

5. 管道的特殊场景

管道有四种特殊场景,即管道为空,管道为满,写端关闭,读端关闭,前两种场景比较简单,为空会造成读端阻塞,未满会造成写端阻塞,来演示一下后两种场景

1、关闭写端,读端没有关闭

#include <iostream>
#include <unistd.h>
#include <cerrno>
#include <cstring>
#include <cassert>
#include <cstdlib>
#include <string>
#include <stdio.h>
#include <sys/types.h>
using namespace std;

int main()
{
   
   
    //1. 创建匿名管道
    int pipefd[2] = {
   
   0};
    int ret = pipe(pipefd);
    if(ret == -1)
    {
   
   
        cout << "pipe failed, errno: " << errno << strerror(errno) << endl;
    }

    cout << "pipefd[0]:" << pipefd[0] << endl;  //fd:3 -> 读端 
    cout << "pipefd[1]:" << pipefd[1] << endl;  //fd:4 -> 写端

    //2. 创建子进程
    pid_t id = fork();
    assert(id != -1);
    if(id == 0) //子进程(写入)
    {
   
   
        close(pipefd[0]); //关闭读端

        //4. 开始通信
         int cnt = 0;
        while(true)
        {
   
   
            char x = 'A';
            write(pipefd[1], &x, 1);
            cout << "cnt:" << cnt << endl;
            sleep(1);
            break;
        }
        close(pipefd[1]);
        exit(0);    
    }

    //父进程(读取):
    //3. 关闭不需要的fd(父进程读取,子进程写入)
    close(pipefd[1]); //关闭写端
    char buffer_parent[1024];
    while(true)
    {
   
   
        int n = read(pipefd[0], buffer_parent, sizeof(buffer_parent) - 1);
        if(n == -1)
        {
   
   
            cout << "read is anomaly" << endl;
        }
        if(n == 0)
        {
   
   
            cout << "read end of file" << endl;
            break;
        }
        buffer_parent[n] = '\0';
        cout << "我是父进程, 子进程给我传递的信息是:" << buffer_parent << endl;
    }
    close(pipefd[0]);
    return 0;
}

关闭写端,读端没有关闭,读端再去读取数据,read就会返回0,也就是读到了文件结尾

  • 管道是单流向通信,写端关闭也就不会再有数据写入,因此当读端把剩余数据都读取后,每次都是读取0字节数据,表明此时已经读到了结尾,读端也就结束读取了

2、关闭读端,写端没有关闭

#include <iostream>
#include <unistd.h>
#include <cerrno>
#include <cstring>
#include <cassert>
#include <cstdlib>
#include <string>
#include <stdio.h>
#include <sys/types.h>
using namespace std;

int main()
{
   
   
    //1. 创建管道
    int pipefd[2] = {
   
   0};
    int fd_result = pipe(pipefd);
    if(fd_result == -1)
    {
   
   
        cout << "pipe failed, errno: " << errno << strerror(errno) << endl;
    }
    cout << "pipefd[0]:" << pipefd[0] << endl;  //fd:3 -> 读端 
    cout << "pipefd[1]:" << pipefd[1] << endl;  //fd:4 -> 写端

    //2. 创建子进程
    pid_t id = fork();
    assert(id != -1);
    if(id == 0) //子进程(写入)
    {
   
   
        close(pipefd[0]); //关闭读端

        //4. 开始通信
        int cnt = 0;
        while(true)
        {
   
   
            char x = 'A';
            write(pipefd[1], &x, 1);
            cout << "cnt:" << cnt << endl;
            sleep(1);
        }
        close(pipefd[1]);
        exit(0);    
    }

    //父进程(读取):
    //3. 关闭不需要的fd(父进程读取,子进程写入)
    close(pipefd[1]); //关闭写端
    char buffer_parent[1024];
    while(true)
    {
   
   
        //sleep(10);
        int n = read(pipefd[0], buffer_parent, sizeof(buffer_parent) - 1);
        if(n == -1)
        {
   
   
            cout << "read is anomaly" << endl;
            break;
        }
        if(n == 0){
   
   
            cout << "read end of file" << endl;
            break;
        }
        buffer_parent[n] = '\0';
        cout << "我是父进程, 子进程给我传递的信息是:" << buffer_parent << endl;
        sleep(1);
        break;
    }
    close(pipefd[0]);
    return 0;
}

关闭读端,写端没有关闭,这样就没有意义,OS不会允许浪费资源的行为,OS会通过信号直接kill掉一直在写入的进程,13号信号就是用来终止管道写端进程的

6. 匿名管道进程池

实现功能:

  • 父进程创建一批子进程,通过多条匿名管道与它们链接通信,向子进程下达指定的任务

实现模型

实现源码

Task.hpp

#include <iostream>
#include <assert.h>
#include <unistd.h>
#include <vector>
#include <string>
#include <sys/types.h>
#include <sys/wait.h>

class manage_child;
class Task;

#define CHILD_PROCESS_NUM 3

void create_frame(std::vector<manage_child>& manage);
void control_child_process(const std::vector<manage_child>& manage);
void recycle_process(const std::vector<manage_child>& manage);

class manage_child
{
   
   
public:
    manage_child(int fd, int id)
        :_write_fd(fd)
        ,_child_id(id)
    {
   
   }

    ~manage_child(){
   
   }
public:
    int _write_fd;
    pid_t _child_id;
};

typedef void(*fun_p)();
#define GET_NET 1
#define POLL_SERVER 2
#define PUSH_SOURCE 3
void get_net();
void poll_server();
void push_source();

class Task
{
   
   
public:
    Task()
    {
   
   
        funcs.push_back(get_net);
        funcs.push_back(poll_server);
        funcs.push_back(push_source);
    }

    void execute_task(int option){
   
   
        assert(option >= 1 && option <= 3);
        funcs[option - 1]();
    }

    ~Task(){
   
   }
private:
    std::vector<fun_p> funcs;
};

ctrlProcess.cc

#include "ctrlProcess.hpp"

void get_net()
{
   
   
    std::cout << "子进程:PID:" << getpid() <<  "获取网络资源中........." << std::endl;
}
void poll_server()
{
   
   
    std::cout << "子进程:PID:" << getpid() <<  "下载服务资源中........" << std::endl;
}
void push_source()
{
   
   
    std::cout << "子进程:PID:" << getpid() <<  "发送服务资源中........" << std::endl;
}

void execute_command(int read_fd)
{
   
   
    Task task;
    while(true){
   
   
        int command = 0;
        int read_return = read(read_fd, &command, sizeof(int));
        if(read_return == sizeof(int)){
   
   
            //执行任务
            task.execute_task(command);
        }else{
   
   
            break;
        }
    }
}

void create_frame(std::vector<manage_child>& manage)
{
   
   
    std::vector<int> fds;
    for(int i = 0; i < CHILD_PROCESS_NUM; ++i){
   
    
        //创建管道
        int pipefd[2];
        int pipe_return = pipe(pipefd);
        assert(pipe_return == 0);
        (void)pipe_return;
        //创建子进程
        pid_t id = fork();
        assert(id != -1);
        if(id == 0){
   
    //子进程 - 读
            for(auto& fd : fds) close(fd); //关闭子进程拷贝父进程的写端
            close(pipefd[1]);
            //dup2(pipefd[0], 0);
            execute_command(pipefd[0]);
            close(pipefd[0]);
            exit(0);
        }
        //父进程
        close(pipefd[0]);
        //父进程对子进程和写端组织管理
        manage.push_back(manage_child(pipefd[1], id)); 
        fds.push_back(pipefd[1]);
    }
}

void show_option(){
   
   
    std::cout << "-----------------------------------------" << std::endl;
    std::cout << "---  1. 获取网络资源   2. 下载服务资源  ---" << std::endl;
    std::cout << "---  3. 发送服务资源   4. 退出服务系统  ---" << std::endl;
    std::cout << "-----------------------------------------" << std::endl;
}

void control_child_process(const std::vector<manage_child>& manage)
{
   
   
    while(true){
   
   
        //选择任务
        show_option();  
        std::cout << "请选择->";
        int command = 0;
        std::cin >> command;
        if(command == 4) break;
        if(command < 1 || command > 3) continue;
        //选择进程
        int rand_index = rand() % manage.size();
        std::cout << "被选中的子进程:" << manage[rand_index]._child_id << std::endl;
        //分发任务
        write(manage[rand_index]._write_fd, &command, sizeof(command));
        sleep(1);
    }   
}

void recycle_process(const std::vector<manage_child>& manage)
{
   
   
    for(int i = 0; i < manage.size(); ++i){
   
   
        std::cout << "父进程让子进程退出" << manage[i]._child_id << std::endl;
        close(manage[i]._write_fd);
        waitpid(manage[i]._child_id, nullptr, 0); //阻塞式等待
        std::cout << "父进程回收子进程:" << manage[i]._child_id << std::endl;
    }
    sleep(5); //父进程退出
}

int main()
{
   
   
    //创建框架:一个父进程控制多个子进程(父进程写,子进程读)
    std::vector<manage_child> manage; 
    create_frame(manage);

    //父进程控制任意子进程执行任务
    control_child_process(manage);

    //回收子进程并关闭管道
    recycle_process(manage);
    return 0;
}

7. 命名管道

命名管道的特点是自带同步与互斥机制、数据单向流通,与匿名管道不同的是,命名管道有自己的名字,因此可以被没有血缘关系的进程看到,所以命名管道可以实现两个独立进程间通信

7.1 概念理解

命名管道(named pipe)又被称为先进先出队列(FIFO),是一种特殊的管道,存在于文件系统中。命名管道与管道非常类似,但是又有自身的显著特征:

  • 命名管道可以用于任何两个进程间的通信,而不限于同源的两个进程
  • 命名管道作为一种特殊的文件存放在文件系统中,而不是像匿名管道那样存放在内核中。当进程对命名管道的使用结束后,命名管道依然存在于文件系统中,除非对其进行删除操作,否则该命名管道不会自行消失
  • 命名管道就是给匿名管道起名字,给匿名管道这个内存文件分配inode,将文件名与之构建联系,但是不给它分配 Data block,它是一个内存文件不需要将数据刷盘到磁盘中

7.2 创建和使用

使用系统调用接口mkfifo(),来创建命名管道

#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);

mkfifo()函数

  • pathname参数:创建命名管道文件时的路径和名字,既可以传递绝对路径也可以传递相对路径
  • mode参数:创建命令管道文件时的权限,mode_t类型就是对 unsigned int的封装,等价于uint32_t
  • 返回值:创建成功返回0,失败则返回-1

mkfifo还可以直接在命令行创建命名管道文件

mkfifo 命名管道文件名  //创建命名管道文件

可见管道文件的类型为p,大小为0,这也就说明管道文件就是一个内存级文件,有自己的上限,在文件系统中只是挂了个名

使用系统调用接口unlink(),来删除命名管道

#include <unistd.h>

int unlink(const char *pathname);

返回值和参数作用和mkfifo()相同,用于删除指定命名管道

当然也可以在命令行使用

unlink 命令管道文件名    //删除命名管道文件

7.3 不同进程间通信

我们可以利用命名管道来模拟两个进程间的通信

  • 服务端server创建管道文件,以读的方式打开,客户端client以写的方式打开管道文件,打开后让两个进程通信,通信结束后,客户端关闭写端,服务端读取到0后关闭并删除命名管道文件

common.hpp 公共资源

#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <string>
#include <assert.h>
#include <fcntl.h>
#include <unistd.h>
#include <cstring>
#include <strings.h>

const std::string path_name = "./fifo"; //管道名
#define MODE 0664 //权限

client.cc

#include "common.hpp"

int main()
{
   
   
    //客户端
    //打开文件
    int open_fd = open(path_name.c_str(), O_WRONLY);
    assert(open_fd != -1);

    //写入数据,进行通信
    while(true){
   
   
        char buffer[1024];
        if(strcasecmp(buffer, "quit") == 0){
   
   
            break;
        }
        std::cout << "请输入:>";
        char* message = fgets(buffer, sizeof(buffer), stdin);
        buffer[strlen(buffer) - 1] = '\0';
        assert(message != nullptr);
        (void)message;
        write(open_fd, buffer, strlen(buffer));
    }
    close(open_fd);

    return 0;
}

sercer.cc

#include "common.hpp"

int main()
{
   
   
    //服务端
    //创建命名管道
    umask(0);
    int mkfifo_ret = mkfifo(path_name.c_str(), MODE);
    assert(mkfifo_ret != -1);

    //打开文件并读取
    int open_fd = open(path_name.c_str(), O_RDONLY);
    assert(open_fd != -1);

    while(true){
   
   
        char buffer[1024];
        ssize_t size = read(open_fd, buffer, sizeof(buffer) - 1);
        if(size > 0){
   
   
            buffer[size] = '\0';
            std::cout << buffer << std::endl;
        }else{
   
   
            break;
        }
    }

    close(open_fd);

    //关闭命名管道
    int unlink_ret = unlink(path_name.c_str());
    assert(unlink_ret != -1);

    return 0;
}

7.4 两种管道的区别

命名管道与匿名管道的区别

  • 匿名管道只能用于具有血缘关系的进程间通信,命名管道谁都可以用
  • 匿名管道直接通过pipe函数创建使用,命名管道需要先通过mkfifo 函数创建,然后再通过open打开使用
  • 出现多条匿名管道时,可能会出现写端fd重复继承的情况,命名管道不会出现这种情况

8. 命名管道进程池

ControlCenter.cc

#include "NamepipePool.hpp"

void create_namepipe()
{
   
   
    int fifo_net_request_ret = mkfifo(fifo_net_request.c_str(), MODE);
    assert(fifo_net_request_ret != -1);
    int fifo_dispose_data_ret = mkfifo(fifo_dispose_data.c_str(), MODE);
    assert(fifo_dispose_data_ret != -1);
    int fifo_manage_system_ret = mkfifo(fifo_manage_system.c_str(), MODE);
    assert(fifo_manage_system_ret != -1);
}

Manage_fifo manage_namepipe()
{
   
   
    std::cout << "打开fifo_net_request......" << std::endl;
    sleep(1);
    int fifo_net_request_fd = open(fifo_net_request.c_str(), O_RDWR);
    assert(fifo_net_request_fd != -1);
    std::cout << "打开fifo_dispose_data......." << std::endl;
    sleep(1);
    int fifo_dispose_data_fd = open(fifo_dispose_data.c_str(), O_RDWR);
    assert(fifo_dispose_data_fd != -1);
    std::cout << "打开fifo_manage_system........" << std::endl;
    sleep(1);
    int fifo_manage_system_fd = open(fifo_manage_system.c_str(), O_RDWR);
    assert(fifo_manage_system_fd != -1);
    Manage_fifo center(   fifo(fifo_net_request_fd, fifo_net_request)
                        , fifo(fifo_dispose_data_fd,fifo_dispose_data)
                        , fifo(fifo_manage_system_fd,fifo_manage_system));
    std::cout << "命名管道全部打开!" << std::endl;
    return center;
}

void task_menu()
{
   
   
    std::cout << "----------------------------------------------" << std::endl;
    std::cout << "-- 1.网络请求                     2.数据处理 --" << std::endl;
    std::cout << "-- 3.系统管理                     4.退出中控 --" << std::endl;
    std::cout << "----------------------------------------------" << std::endl;
}

void write_fifo(int& command, Manage_fifo& center)
{
   
   
    int buf = command;
    for(int i = 0; i < PROCESS_NUM; ++i){
   
   
        if(command - 1 == i){
   
   
            write(center._fifos[i]._fifo_fd, &buf, sizeof(buf));
            break;
        }
    }
}

void close_fifo(Manage_fifo& center)
{
   
   
    for(int i = 0; i < PROCESS_NUM; ++i){
   
   
        close(center._fifos[i]._fifo_fd);
        unlink(center._fifos[i]._fifo_name.c_str());
    }
}

//主控进程管理命令管道写入->任务进程读取执行
int main()
{
   
   
    //1. 创建管道文件
    create_namepipe(); 
    //2. 打开文件做管理
    Manage_fifo center;
    try
    {
   
   
        center = manage_namepipe(); //TODO(拷贝构造)
    }
    catch(const std::exception& e)
    {
   
   
        std::cerr << e.what() << '\n';
    }

    std::cout << "开始执行任务" << std::endl;
    //3. 通过选择执行任务
    while(true)
    {
   
   
        //1. 选择执行任务
        // > 列出菜单
        task_menu();
        // > 输入
        int command = 0;
        std::cout << "请求输入#";
        std::cin >> command;
        if(command == 4){
   
   
            break;
        }
        if(command < 1 || command > 4){
   
   
            std::cout << "输入错误,请重新输入!";
            continue;
        }
        //2. 执行选择写入对应进程
        write_fifo(command, center);
        sleep(1);
    }
    //3. 关闭所有命名管道
    close_fifo(center);
    (void)center;
    return 0;
}

DisposeData.cc

#include "NamepipePool.hpp"

int main()
{
   
   
    int fd = open(fifo_dispose_data.c_str(), O_RDWR);
    assert(fd != -1);
    int buf = 0;
    ssize_t size = read(fd, &buf, sizeof(buf));
    assert(size >= 0);
    if(buf == FIFO_DISPOSE_DATA){
   
   
        int cnt = 5;
        while(cnt > 0)
        {
   
   
            printf("数据库正在处理数据中..............(请等待<%d>秒钟)\n", cnt--);
            sleep(1);
        }
    }    
    if(size == -1){
   
   
        std::cout << "dispose_data进程读取错误";
        exit(0);
    }
    return 0;
}

ManageSystem.cc

#include "NamepipePool.hpp"

int main()
{
   
   
    int fd = open(fifo_manage_system.c_str(), O_RDWR);
    assert(fd != -1);
    int buf = 0;
    while(true)
    {
   
   
        ssize_t size = read(fd, &buf, sizeof(buf));
        assert(size >= 0);
        if(buf == FIFO_MANAGE_SYSTEM){
   
   
            int cnt = 5;
            while(cnt > 0)
            {
   
   
                printf("系统正在管理各个子系统中..............(请等待<%d>秒钟)\n", cnt--);
                sleep(1);
            }
            break;
        } 
        if(size == -1){
   
   
            std::cout << "manage_system进程读取错误";
            exit(0);
        } 
    }


    return 0;
}

NamepipePool.hpp

#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <string>
#include <vector>
#include <unistd.h>
#include <fcntl.h>
#include <cstring>
#include <assert.h>

//管道名
std::string fifo_net_request = "./fifo_net_request";
std::string fifo_dispose_data = "./fifo_dispose_data";
std::string fifo_manage_system = "./fifo_manage_system";
#define MODE 0664
#define FIFO_NET_REQUEST 1
#define FIFO_DISPOSE_DATA 2
#define FIFO_MANAGE_SYSTEM 3
#define PROCESS_NUM 3


class fifo
{
   
   
public:
    fifo(const int& fd, std::string& name)
        :_fifo_fd(fd)
        ,_fifo_name(name)
    {
   
   }
    fifo(const fifo& x)
    {
   
   
        _fifo_fd = x._fifo_fd;
        _fifo_name = x._fifo_name;
    }
    ~fifo(){
   
   }
public:
    int _fifo_fd;
    std::string _fifo_name;
};

class Manage_fifo
{
   
   
public:
    Manage_fifo(){
   
   }

    Manage_fifo(const fifo fd1, const fifo fd2, const fifo fd3)
    {
   
   
        _fifos.push_back(fd1);
        _fifos.push_back(fd2);
        _fifos.push_back(fd3);
    }

    ~Manage_fifo(){
   
   }
public:
    std::vector<fifo> _fifos;
};

NetRequest.cc

#include "NamepipePool.hpp"

int main()
{
   
   
    int fd = open(fifo_net_request.c_str(), O_RDWR);
    assert(fd != -1);
    int buf = 0;
    ssize_t size = read(fd, &buf, sizeof(buf));
    assert(size > 0);
    if(buf == FIFO_NET_REQUEST){
   
   
        int cnt = 5;
        while(cnt > 0)
        {
   
   
            printf("网络正在请求中..............(请等待<%d>秒钟)\n", cnt--);
            sleep(1);
        }
    }    
    if(size == -1){
   
   
        std::cout << "net_request进程读取错误";
        exit(0);
    }
    return 0;
}

Makefile

.PHONY:all
all:ControlCenter ManageSystem NetRequest DisposeData

ControlCenter:ControlCenter.cc
    g++ -o {
   
   mathJaxContainer[0]}^ -std=c++11 -g
ManageSystem:ManageSystem.cc
    g++ -o {
   
   mathJaxContainer[1]}^ -std=c++11
NetRequest:NetRequest.cc
    g++ -o {
   
   mathJaxContainer[2]}^ -std=c++11 -g
DisposeData:DisposeData.cc
    g++ -o {
   
   mathJaxContainer[3]}^ -std=c++11

.PHONY:clean
clean:
    rm -f ControlCenter ManageSystem NetRequest DisposeData 
    rm -f fifo_*

Linux进程间通信【匿名管道和命名管道】,到这里就介绍结束了,本篇文章对你由帮助的话,期待大佬们的三连,你们的支持是我最大的动力!

文章有写的不足或是错误的地方,欢迎评论或私信指出,我会在第一时间改正

相关实践学习
CentOS 7迁移Anolis OS 7
龙蜥操作系统Anolis OS的体验。Anolis OS 7生态上和依赖管理上保持跟CentOS 7.x兼容,一键式迁移脚本centos2anolis.py。本文为您介绍如何通过AOMS迁移工具实现CentOS 7.x到Anolis OS 7的迁移。
目录
相关文章
|
25天前
|
缓存 监控 Linux
linux进程管理万字详解!!!
本文档介绍了Linux系统中进程管理、系统负载监控、内存监控和磁盘监控的基本概念和常用命令。主要内容包括: 1. **进程管理**: - **进程介绍**:程序与进程的关系、进程的生命周期、查看进程号和父进程号的方法。 - **进程监控命令**:`ps`、`pstree`、`pidof`、`top`、`htop`、`lsof`等命令的使用方法和案例。 - **进程管理命令**:控制信号、`kill`、`pkill`、`killall`、前台和后台运行、`screen`、`nohup`等命令的使用方法和案例。
96 4
linux进程管理万字详解!!!
|
16天前
|
存储 运维 监控
深入Linux基础:文件系统与进程管理详解
深入Linux基础:文件系统与进程管理详解
57 8
|
13天前
|
Linux
如何在 Linux 系统中查看进程占用的内存?
如何在 Linux 系统中查看进程占用的内存?
|
25天前
|
算法 Linux 定位技术
Linux内核中的进程调度算法解析####
【10月更文挑战第29天】 本文深入剖析了Linux操作系统的心脏——内核中至关重要的组成部分之一,即进程调度机制。不同于传统的摘要概述,我们将通过一段引人入胜的故事线来揭开进程调度算法的神秘面纱,展现其背后的精妙设计与复杂逻辑,让读者仿佛跟随一位虚拟的“进程侦探”,一步步探索Linux如何高效、公平地管理众多进程,确保系统资源的最优分配与利用。 ####
66 4
|
26天前
|
缓存 负载均衡 算法
Linux内核中的进程调度算法解析####
本文深入探讨了Linux操作系统核心组件之一——进程调度器,着重分析了其采用的CFS(完全公平调度器)算法。不同于传统摘要对研究背景、方法、结果和结论的概述,本文摘要将直接揭示CFS算法的核心优势及其在现代多核处理器环境下如何实现高效、公平的资源分配,同时简要提及该算法如何优化系统响应时间和吞吐量,为读者快速构建对Linux进程调度机制的认知框架。 ####
|
27天前
|
消息中间件 存储 Linux
|
Linux Shell
详解linux进程间通信-管道 popen函数 dup2函数
  前言:进程之间交换信息的唯一方法是经由f o r k或e x e c传送打开文件,或通过文件系统。本章将说明进程之间相互通信的其他技术—I P C(InterProcess Communication)。
1594 0
|
15天前
|
监控 Linux
如何检查 Linux 内存使用量是否耗尽?这 5 个命令堪称绝了!
本文介绍了在Linux系统中检查内存使用情况的5个常用命令:`free`、`top`、`vmstat`、`pidstat` 和 `/proc/meminfo` 文件,帮助用户准确监控内存状态,确保系统稳定运行。
103 6
|
16天前
|
Linux
在 Linux 系统中,“cd”命令用于切换当前工作目录
在 Linux 系统中,“cd”命令用于切换当前工作目录。本文详细介绍了“cd”命令的基本用法和常见技巧,包括使用“.”、“..”、“~”、绝对路径和相对路径,以及快速切换到上一次工作目录等。此外,还探讨了高级技巧,如使用通配符、结合其他命令、在脚本中使用,以及实际应用案例,帮助读者提高工作效率。
57 3
|
16天前
|
监控 安全 Linux
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景,包括 ping(测试连通性)、traceroute(跟踪路由路径)、netstat(显示网络连接信息)、nmap(网络扫描)、ifconfig 和 ip(网络接口配置)。掌握这些命令有助于高效诊断和解决网络问题,保障网络稳定运行。
48 2