Linux中的进程间通信之管道

news2024/10/3 16:51:05

管道

管道是Unix中最古老的进程间通信的形式。

我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”

匿名管道

#include <unistd.h>
功能:创建一无名管道
原型
int pipe(int fd[2]);
参数
fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端
返回值:成功返回0,失败返回错误代码

管道文件时一种内存级文件,没有名称,故为匿名管道

父进程打开文件然后fork创建子进程,子进程会继承父进程文件描述符表中的内容。

然后两个进程即可看到同一份文件。

一般而言管道只能用来进行单向的数据通信。

为什么让父进程以读和写的方式打开同一个文件?

为了让子进程看到读写端。

int fds[2];
    int n=pipe(fds);

代码实现 

    //1.创建管道文件打开读写端
    int fds[2];
    int n=pipe(fds);
    assert(n==0);
    //2.fork
    pid_t id=fork();
    assert(id>=0);
    if(id==0)
    {
        //子进程写入
        close(fds[0]);
        const char* s="子进程,正在向父进程发消息";
        int cnt=0;
        while(true)
        {
            cnt++;
            char buffer[1024];
            snprintf(buffer,sizeof buffer,"child->parent say: %s[%d][%d]",s,cnt,getpid());
            write(fds[1],buffer,sizeof(buffer));//系统接口不需要考虑'\0'
            sleep(1);
        }
        close(fds[1]);
        exit(0);
    }
    close(fds[1]);
    while(true)
    {
        char buffer[1024];
        ssize_t s=read(fds[0],buffer,sizeof(buffer)-1);
        if(s>0)//s代表读到的字节数
            buffer[s]=0;
        cout<<"Get Message # "<<buffer<<"| mypid: "<<getpid()<<endl;
    }

    n=waitpid(id,nullptr,0);
    assert(n==id);
    close(fds[0]);

命名管道

命名管道可以从命令行上创建,命令行方法是使用下面这个命令:

mkfifo filename

命名管道也可以从程序里创建,相关函数有:

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

创建命名管道:

int main(int argc, char *argv[])
{
 mkfifo("p2", 0644);
 return 0;
}

comm.hpp

#pragma once

#include<iostream>
#include<cassert>
#include<cstdio>
#include<cstring>
#include<string>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<cerrno>
#include<fcntl.h>
using namespace std;
#define NAMED_PIPE "/tmp/mypipe.test"

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

server.cc

#include"comm.hpp"

using namespace std;

int main()
{
    bool r=createFifo(NAMED_PIPE);
    assert(r);
    cout<<"server begin"<<endl;
    int rfd=open(NAMED_PIPE,O_RDONLY);
    cout<<"server end"<<endl;
    if(rfd<0) exit(1);
    //read
    char buffer[1024];
    while (true)
    {
        ssize_t s=read(rfd,buffer,sizeof(buffer)-1);
        if(s>0)
        {
            buffer[s]=0;
            cout<<"client->server# "<<buffer;
        }
        else if(s==0)
        {
            cout<<"client quit , me too"<<endl;
            break;
        }
        else
        {
            cout<<"err string--------"<<strerror(errno)<<endl;
            break;
        }
    }
    
    close(rfd);
    removeFifo(NAMED_PIPE);
    return 0;
}

clent.cc

#include"comm.hpp"

using namespace std;

int main()
{
    cout<<"client begin"<<endl;
    int wfd = open(NAMED_PIPE, O_WRONLY);
    cout<<"client end"<<endl;
    if (wfd < 0)
        exit(1);
    // write
    char buffer[1024];
    while (true)
    {
        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));
    }
    close(wfd);
    return 0;
}

管道的读写特征

1. (读慢,写快)如果管道中没有了数据,读端在读,默认会直接阻塞当前正在读取的进程

2.(读快,写慢)管道是一个固定大小的缓冲区,写端写满时会阻塞,等待对方读取

3.(写端关闭,读到0)读端将数据读完后读到0结束进程。

4.读关闭,在写就没有意义了,OS会给写进程发送信号(13),将其终止

管道的特征

1.管道的声明周期随进程

2.管道可以用来进行具有血缘关系的进程之间进行通信,常用于父子通信

3.管道是面向字节流的。按设置的最大字节数去读。

4.管道通信---半双工

sleep 1000 | sleep 2000

|:即为匿名管道

sleep的父进程为bash

综合案例

基于匿名管道的进程池设计

#include<iostream>
#include<vector>
#include<cassert>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<string>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>

using namespace std;
#define PROCESS_NUM 5
#define MakeSeed() srand((unsigned long)time(nullptr))
typedef void(*func_t)(); 
void downLoadTask()
{
    cout<<getpid()<<" : downLoadTask()\n"<<endl;
    sleep(1);
}
void ioTask()
{
    cout<<getpid()<<" : ioTask()\n"<<endl;
    sleep(1);
}
void flushTask()
{
    cout<<getpid()<<" : flustTask()\n"<<endl;
    sleep(1);
}

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;
    }
public:
    static int num;
    string name_;
    pid_t subId_;
    int writeFd_;
};
int subEp::num=0;

void sendTask(const subEp& process,int taskNum)
{
    cout<<"send task num   "<<taskNum<<" send to -> "<<process.name_<<endl;
    ssize_t n=write(process.writeFd_,&taskNum,sizeof(taskNum));
    assert(n==sizeof(int));
}
int recvTask(int readFd) 
{
    int code=0;
    ssize_t s=read(readFd,&code,sizeof code);
    //assert(s==sizeof(int));
    if(s==4) return code;
    else if(s<=0) return -1;
    else return 0;
}
void createSubProcess(vector<subEp>* subs,vector<func_t>& funcMap)
{
    for(int i=0;i<PROCESS_NUM;++i)
    {
        int fds[2];
        int n=pipe(fds);
        //父进程打开的文件是会被子进程共享的
        pid_t id=fork();
        if(id==0)
        {
            //子进程
            close(fds[1]);
            while (true)
            {
                //1.获取命令玛,如果没有,子进程应阻塞
                int commandCode = recvTask(fds[0]);
                //2.完成任务
                if(commandCode>0 && commandCode<funcMap.size()) funcMap[commandCode]();
                else if(commandCode==-1) break;;
            }
            exit(0);
        }
        close(fds[0]);
        subEp sub(id,fds[1]);
        subs->push_back(sub);
    }
}
void loadTaskFunc(vector<func_t>* out)
{
    assert(out);
    out->push_back(downLoadTask);
    out->push_back(ioTask);
    out->push_back(flushTask);
}
void loadBalanceContrl(const vector<subEp>& subs,const vector<func_t> &funcMap,int count)
{
    int processnum=subs.size();
    int tasknum=funcMap.size();
    //int cnt=subs.size();
    bool forever=(count==0)?true:false;
    while(true)
    {
        //1.选择一个子进程--->vector<subEp> -> index
        int subIdx=rand()%processnum;
        //2.选择一个任务---> vector<func_t>--->index
        int taskIdx=rand()%tasknum;
        //3.任务发送给选择的进程
        sendTask(subs[subIdx],taskIdx);
        sleep(1);
        if(!forever)
        {
            count--;
            if(count==0) break;
        }
    }
    //write quit -> read 0
    for(int i=0;i<processnum;++i) close(subs[i].writeFd_);
}
void waitProcess(vector<subEp> processes)
{
    int processnum=processes.size();
    for(int i=0;i<processnum;++i)
    {
        waitpid(processes[i].subId_,nullptr,0);
        cout<<"wait sub process success ..."<<processes[i].subId_<<endl;
    }
}
int main()
{
    MakeSeed();
    //1.建立子进程并建立和子进程通信的信道
    vector<func_t> funcMap;
    loadTaskFunc(&funcMap);
    vector<subEp> subs;
    createSubProcess(&subs,funcMap);
    //2.父进程控制子进程
    int taskNum=3;
    loadBalanceContrl(subs,funcMap,taskNum);
    //3.回收子进程信息
    waitProcess(subs);
    return 0;
}

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

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

相关文章

p2p、分布式,区块链笔记: Libp2p分散打孔 2022 IEEE 第 42 届分布式计算系统国际会议研讨会 (ICDCSW)

Decentralized Hole Punching 我们提出了一种内置于对等网络库 libp2p [1] 中的去中心化打洞机制。打洞对于对等网络至关重要&#xff0c;它使每个参与者能够直接与任何其他参与者通信&#xff0c;尽管被防火墙和 NAT 隔开。去中心化的 libp2p 打洞协议利用了类似于 STUN&…

Vivado - BD(差分时钟、简单分频、RESET、KEY)

目录 1. 简介 1.1 要点 1.2 buffer 介绍 2. vivado 工程 2.1 Block Design 2.2 IBUFDS 2.3 BUFGCE_DIV 2.4 Processor System Reset 2.5 key_mod 2.6 led_drv 3. 编译与调试 3.1 XDC 3.2 Debug 4. 总结 1. 简介 1.1 要点 了解 Utility Buffer v2.2 中的 Buffer…

关于HTML 案例_个人简历展示01

案例效果展示 代码 <!DOCTYPE html> <lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>个人简历信息</title> </he…

C语言高阶【1】--动态内存管理【1】(可以灵活的申请和使用内存,它不香吗?)

本章概述 为什么要有动态内存分配&#xff1f;malloc函数和free函数calloc函数和realloc函数常见的动态内存的错误彩蛋时刻&#xff01;&#xff01;&#xff01; 为什么要有动态内存分配&#xff1f; 情况描述&#xff1a;当我们创建一个变量时&#xff0c;比如&#xff0c;i…

《中安未来护照阅读器 —— 机场高效通行的智慧之选》

在机场&#xff0c;高效与准确的旅客信息处理至关重要。中安未来护照阅读器&#xff0c;为机场带来全新的智能化体验。它能够快速准确地读取护照信息&#xff0c;自动识别多种证件类型&#xff0c;极大提高了值机、安检等环节的效率。无论是繁忙的国际航站楼&#xff0c;还是国…

51单片机的串口

目录 一、串口的介绍 1、硬件电路 二、51单片机的UART 1、串口参数及时序图 2、串口模式图 3、串口和中断系统结构图 4、串口相关寄存器 三、串口向电脑发送数据 1、通过STC-ISP软件 四、电脑通过串口控制LED 1、主函数 2、 UART串口通信模块 一、串口的介绍 串口是一…

倒排索引是什么

倒排索引 简单了解&#xff1a; 什么是正向索引? 基于文档id创建索引。查询词条时必须先找到文档&#xff0c;而后判断是否包含词条 什么是倒排索引? 对文档内容分词&#xff0c;对词条创建索引&#xff0c;并记录词条所在文档的信息。查询时先根据词条查询到文档id&#…

C++和OpenGL实现3D游戏编程【连载13】——多重纹理混合详解

🔥C++和OpenGL实现3D游戏编程【目录】 1、本节要实现的内容 前面说过纹理贴图能够大幅提升游戏画面质量,但纹理贴图是没有叠加的。在一些游戏场景中,要求将非常不同的多个纹理(如泥泞的褐色地面、绿草植密布的地面、碎石遍布的地面)叠加(混合)起来显示,实现纹理间能…

WPS(金山文档)与金蝶云星空通过HTTP实现连接

WPS(金山文档)通过HTTP与金蝶云星空实现数据互通 该方式不需要通过金蝶SDK webapi官方文档地址&#xff1a;https://vip.kingdee.com/article/407944297573586944?langzh-CN&productLineId1&isKnowledge2 一、两种方式 airscript脚本发送http请求和PY脚本编辑器发送…

SCoRe: 通过强化学习教导大语言模型进行自我纠错

大语言模型(LLMs)在推理任务中,如数学问题求解和编程,已经展现出了优秀的性能。尽管它们能力强大,但在实现能够通过计算和交互来改进其回答的算法方面仍然面临挑战。现有的自我纠错方法要么依赖于提示工程,要么需要使用额外的模型进行微调,但这些方法都有局限性,往往无法产生有…

Java项目实战II基于Java+Spring Boot+MySQL的海滨体育馆管理系统的设计与实现(源码+数据库+文档)

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者 一、前言 随着健康意识的提升和体育运动的普及&#xff0c;海滨体育馆作为集休闲、健身、娱乐于一体的综合性场…

MongoDB伪分布式部署(mac M2)

1. 序言 本博客是上一博客的进阶版&#xff1a;mac M2安装单机版 MongoDB 7.x&#xff0c;上一博客可以看做是单机、单节点部署MongoDB本博客将介绍单机、多服务部署MongoDB&#xff0c;实际就是伪分布式部署 2. 副本集(Replica Set)方式部署 2.1 什么是副本集&#xff1f; …

DNS与ICMP

一、DNS 在TCP/IP协议中&#xff0c;要用IP地址和端口号来连接服务器&#xff0c;但是直接输入数字用户体验感不好&#xff0c;所以就发明了主机号&#xff08;字符串&#xff09;&#xff0c;用host文件作主机名与IP的映射关系储存。 随着数量的增多&#xff0c;手动管理太麻…

【机器学习】集成学习——提升模型准确度的秘密武器

【机器学习】集成学习——提升模型准确度的秘密武器 1. 引言 集成学习&#xff08;Ensemble Learning&#xff09;是一种通过结合多个弱模型来提升整体预测准确性的技术。通过将多个模型的预测结果进行组合&#xff0c;集成学习在复杂任务中展现了极强的泛化能力。本文将探讨…

基于微信的乐室预约小程序+ssm(lw+演示+源码+运行)

摘 要 随着社会的发展&#xff0c;社会的方方面面都在利用信息化时代的优势。互联网的优势和普及使得各种系统的开发成为必需。 本文以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法&#xff0c;它主要是采用java语言技术和mysql数据库来完成对系统的设计。整个…

深入探讨指令调优的局限性

深入探讨指令调优的局限性 摘要 指令调优&#xff08;Instruction Tuning, IT&#xff09;是通过使用指令-响应对训练大型语言模型&#xff08;LLMs&#xff09;的过程&#xff0c;已成为将基础预训练LLMs转化为开放领域对话代理的主要方法。尽管IT取得了显著的成功和广泛的应…

HUAWEI New4.9G 与 2.6G 无法正常切换问题处理案例

HUAWEI New4.9G 与 2.6G 无法正常切换问题处理案例 在某地市的 XX 音乐节保障准备期间&#xff0c;为确保活动期间的网络质量&#xff0c;现场新开了 4.9G HUAWEI 室外基站。在网络优化和测试中&#xff0c;发现UE无法实现从 2.6G 到 4.9G 的正常切换。虽然现场具备 4.9G信号覆…

算法与数据结构--二分查找

原理 简单说就是每次二分取中间值&#xff0c;然后将中间值与我们要找的值比较&#xff0c;如果比它大就移动左边界到其右侧&#xff0c;如果比它小就移动右边界到其左侧。直到中间值等于该值或者经过移动直至右边界在左边界左侧为止。 二.具体实现 public int searchInsert(…

数据结构 ——— 单链表oj题:反转链表

目录 题目要求 手搓一个简易链表 代码实现 题目要求 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表 手搓一个简易链表 代码演示&#xff1a; struct ListNode* n1 (struct ListNode*)malloc(sizeof(struct ListNode)); assert(n1);…

【重学 MySQL】五十三、MySQL数据类型概述和字符集设置

【重学 MySQL】五十三、MySQL数据类型概述和字符集设置 MySQL数据类型概述MySQL字符集设置注意事项 MySQL数据类型概述 MySQL是一个流行的关系型数据库管理系统&#xff0c;它支持多种数据类型&#xff0c;以满足不同数据处理和存储的需求。理解并正确使用这些数据类型对于提高…