pipe匿名管道实操(Linux)

管道相关函数

1 pipe

  • 是 Unix/Linux 系统中的一个系统调用,用于创建一个匿名管道
#include <unistd.h>
int pipe(int pipefd[2]);
参数说明:
pipefd[2]:一个包含两个整数的数组,用于存储管道的文件描述符:
pipefd[0]:管道的读端(用于从管道读取数据)巧记:用嘴巴口型(o)读
pipefd[1]:管道的写端(用于向管道写入数据)巧记:用笔(1)写
返回值:
成功时返回 0
失败时返回 -1 并设置 errno

2 error

errno 是 C 和 C++ 中用于报告错误的全局变量(或宏),全称为 "error number"。它由系统或标准库函数在操作失败时设置,用于指示具体的错误原因。代码出错时我们更想知道出错原因,就可以用error

常见 errno 错误码

错误码宏含义
EPERM1操作无权限
ENOENT2文件或目录不存在
EINTR4系统调用被中断
EIO5输入/输出错误
EBADF9错误的文件描述符
EAGAIN11资源暂时不可用
ENOMEM12内存不足
EACCES13权限不足
EFAULT14非法内存访问
EEXIST17文件已存在
EDOM33数学参数超出定义域
ERANGE34结果超出范围

一般和和strerror配合一起使用 

#include <iostream>
#include <cerrno>  
#include <cstring>int main() {errno = 0; // 先重置 errnodouble x = sqrt(-1.0); // 尝试计算负数的平方根if (errno == EDOM) {   // EDOM 是域错误宏std::cerr << "Error: " << std::strerror(errno) << "\n";}
}
输出:
Error: Numerical argument out of domain

3 strerror 

  •  是 C 标准库中的一个函数,用于将错误代码(errno 值)转换为可读的错误描述字符串。下面我会详细解释它的用法和实际应用场景。
#include <string.h>  
char *strerror(int errnum);
参数说明:
errnum:错误编号(通常是 errno 的值)
返回值:
返回指向错误描述字符串的指针(静态分配的字符串,不可修改)
不会失败(永远返回有效指针)

4 推荐使用 #include <cerrno> 而不是 #include <errno.h>


1. 符合 C++ 标准库的命名规范

C++ 标准库对 C 标准库的头文件进行了重新封装,采用无 .h 后缀的形式(如 <cstdio><cstdlib><cerrno>),以区别于 C 的传统头文件(如 <stdio.h>stdlib.herrno.h>)。

  • <cerrno> 是 C++ 标准化的头文件,明确属于 C++ 标准库。
  • <errno.h> 是 C 风格的头文件,虽然 C++ 兼容它,但不推荐在新代码中使用。

2. 潜在的命名空间管理

理论上,<cerrno> 将相关名称(如 errnoEDOMERANGE)放入 std 命名空间,而 <errno.h> 直接将它们暴露在全局命名空间。虽然实际实现中(由于兼容性要求):

  • errno 仍然是全局宏(无法放入 std)。
  • EDOMERANGE 等宏通常在全局命名空间也可用。

但使用 <cerrno> 能更清晰地表达“这是 C++ 代码”的意图,并可能在未来的标准中更好地支持命名空间隔离。


注意事项

  • errno 仍是全局宏:即使使用 <cerrno>errno 也不会变成 std::errno(因为它是宏)。
  • 错误码宏(如 EDOM:大多数实现仍允许全局访问,但理论上可以额外通过 std::EDOM 访问(尽管实践中很少需要)。

fork() 系统调用详解

  • fork() 是 Unix/Linux 系统中的一个重要系统调用,用于创建一个新的进程(子进程)
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
返回值:
父进程:返回子进程的 PID(进程ID,> 0)。
子进程:返回 0。
出错时:返回 -1(并设置 errno)。

6 exit

  • 是一个标准库函数,用于终止当前进程,并返回一个状态码给操作系统。它是进程正常退出的标准方式
#include <stdlib.h>
void exit(int status);
参数:
status:进程的退出状态码:
0 或 EXIT_SUCCESS:表示成功退出。
非零值(通常 EXIT_FAILURE=1):表示失败退出(具体含义由程序定义)

exit() 的运行机制

(1) 进程终止流程

当调用 exit() 时,操作系统会按顺序执行以下操作:

  1. 调用 atexit() 注册的函数(按注册的逆序执行)。

  2. 刷新所有标准 I/O 缓冲区(如 printf 未输出的内容会被强制写入)。

  3. 关闭所有打开的文件描述符

  4. 释放进程占用的内存和其他资源

  5. 向父进程发送状态码(可通过 wait() 或 $? 获取)。

(2) exit() vs _exit()

函数说明
exit()标准 C 库函数,会执行清理(刷新缓冲区、调用 atexit() 等)。
_exit()系统调用(<unistd.h>),直接终止进程,不执行任何清理

 7 snprintf 

snprintf 是 C 标准库中的一个格式化输出函数,用于安全地格式化字符串并写入缓冲区,比传统的 sprintf 更安全,因为它可以防止缓冲区溢出(Buffer Overflow)

#include <stdio.h>
int snprintf(char *str,       // 目标缓冲区size_t size,     // 缓冲区大小(最多写入 size-1 个字符 + '\0')const char *format,  // 格式化字符串(类似 printf)...              // 可变参数(要格式化的数据)
);
返回值:
成功:返回理论写入的字符数(不包括结尾的 \0),即使缓冲区不够。
错误:返回负值(如编码错误)。

    getpid()  getppid()

    • getpid() 是 Unix/Linux 系统编程中的一个基础系统调用,用于获取当前进程的进程ID(PID)
    • getppid() 是 Unix/Linux 系统调用,用于获取当前进程的父进程 PID(Process ID)
    #include <unistd.h>  // 必须包含的头文件
    pid_t getpid(void);  // 返回当前进程的 PID
    返回值:
    成功:返回当前进程的 PID(正整数)
    不会失败(无错误码)#include <unistd.h>  // 必须包含的头文件
    pid_t getppid(void); // 返回父进程的 PID
    返回值:
    成功:返回父进程的 PID(正整数)
    不会失败(无错误码)

    sizeof 

    sizeof 是 C/C++ 中的一个编译时运算符(不是函数!),用于计算变量、类型或表达式所占的内存大小(字节数)。它是静态计算的,不会在运行时影响程序性能

    sizeof(变量或类型)
    返回值:
    size_t 类型的无符号整数(通常是 unsigned int 或 unsigned long)。
    计算时机:在编译时确定,不会执行括号内的代码(如果传入表达式)

    语法规则

    操作对象示例是否必须加括号备注
    变量名sizeof a可选更简洁,但可能降低可读性
    类型名sizeof(int)必须不加括号会导致编译错误
    表达式sizeof(a + b)必须表达式需用括号包裹

    示例 

    int arr[10];变量(括号可选)
    size_t s1 = sizeof arr;     // 计算数组总大小
    size_t s2 = sizeof(arr);    // 等效写法类型(括号必须)
    size_t s3 = sizeof(int);    // 计算 int 类型大小表达式(括号必须)
    size_t s4 = sizeof(arr[0]); // 计算数组元素大小结构体/类成员的大小
    struct S { int x; double y; };
    size_t s = sizeof(S::x);  // C++ 中合法,计算成员大小

    创建管道实操

    makefile

    mypipe:mypipe.ccg++ -o $@ $^ -std=c++11
    .PHONY:clean
    clean:rm -rf mypipe

    mypipe.cc

    #include <iostream>
    #include <string>
    #include <cerrno>
    #include <cassert>
    #include <string.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <sys/wait.h>int main()
    {int pipefd[2] = {0};int n = pipe(pipefd);if(n < 0){std::cout << "pipe error, " << errno << ": " << strerror(errno) << std::endl;return 1;}pid_t id = fork();assert(id != -1); if(id == 0){close(pipefd[0]);int cnt = 0;while(true){char x = 'X';write(pipefd[1], &x, 1);std::cout << "Cnt: " << cnt++<<std::endl;sleep(1);}close(pipefd[1]);exit(0);}close(pipefd[1]);char buffer[1024];int cnt = 0;while(true){int n = read(pipefd[0], buffer, sizeof(buffer) - 1);if(n > 0){buffer[n] = '\0';std::cout << "我是父进程, child give me message: " << buffer << std::endl;}else if(n == 0){std::cout << "我是父进程, 读到了文件结尾" << std::endl;break;}else {std::cout << "我是父进程, 读异常了" << std::endl;break;}sleep(1);if(cnt++ > 5) break;}close(pipefd[0]);int status = 0;waitpid(id, &status, 0);std::cout << "sig: " << (status & 0x7F) << std::endl;sleep(100);return 0;
    }

     

    • mypipe:目标文件(可执行文件)名称
    • mypipe.cc:依赖文件(源代码文件)
    • g++ -o $@ $^ -std=c++11:编译命令
      • $@ 表示目标文件(mypipe)
      • $^ 表示所有依赖文件(这里只有 mypipe.cc)
      • -std=c++11 指定使用 C++11 标准
    • .PHONY:clean:声明 clean 是一个伪目标(不是实际文件)
    • rm -rf mypipe:删除生成的可执行文件

    父进程管理多个子进程实现管道通信实操

    Makefile

    ctrlProcess:ctrlProcess.ccg++ -o $@ $^ -std=c++11
    .PHONY:clean
    clean:rm -rf ctrlProcess
    

    Task.hpp

    #pragma once#include <iostream>
    #include <vector>
    #include <unistd.h>typedef void (*fun_t)(); void a() { std::cout << "a任务正在执行...\n" << std::endl; }
    void b() { std::cout << "b任务正在执行...\n" << std::endl; }
    void c() { std::cout << "c任务正在执行...\n" << std::endl; }#define A 0
    #define B 1
    #define C 2class Task
    {
    public:Task(){funcs.push_back(a);funcs.push_back(b);funcs.push_back(c);}void Execute(int command){if (command >= 0 && command < funcs.size()) funcs[command]();}public:std::vector<fun_t> funcs;
    };

    ctrlProcess.cc

    #include <iostream>
    #include <string>
    #include <vector>
    #include <cassert>
    #include <unistd.h>
    #include <sys/wait.h>
    #include <sys/types.h>
    #include "Task.hpp"
    using namespace std;const int gnum = 3;
    Task t;class EndPoint
    {
    private:static int number;
    public:pid_t _c_id;int _w_fd;string processname;
    public:EndPoint(int id, int fd) :_c_id(id), _w_fd(fd){//process-0[pid:fd]char namebuffer[64];snprintf(namebuffer, sizeof(namebuffer), "process-%d[%d:%d]", number++, _c_id, _w_fd);processname = namebuffer;}string name() const { return processname; }
    };int EndPoint::number = 0;void WaitCommand()
    {while(1){int command = 0;int n = read(0, &command, sizeof(command));if (n == sizeof(int)) t.Execute(command);else if (n == 0){std::cout << "父进程关闭了写端" << getpid() << std::endl;break;}else break;}
    }void createProcesses(vector<EndPoint> *end_points)
    {vector<int> fds;for(int i = 0; i < gnum; ++i){int pipefd[2] = {0};int n = pipe(pipefd);assert(n == 0); (void)n;pid_t id = fork();assert(id != -1);if (id == 0){for(auto &fd : fds) close(fd);close(pipefd[1]);dup2(pipefd[0], 0);WaitCommand();close(pipefd[0]);exit(0);}close(pipefd[0]);end_points->push_back(EndPoint(id, pipefd[1]));fds.push_back(pipefd[1]);}
    }int ShowBoard()
    {std::cout << "##########################################" << std::endl;std::cout << "|   0. 执行日志任务   1. 执行数据库任务    |" << std::endl;std::cout << "|   2. 执行请求任务   3. 退出             |" << std::endl;std::cout << "##########################################" << std::endl;std::cout << "请选择# ";int command = 0;std::cin >> command;return command;
    }void ctrlProcess(const vector<EndPoint> &end_points)
    {int cnt = 0;while(true){int command = ShowBoard();if (command == 3) break;if (command < 0 || command > 2) continue;int index = cnt++;cnt %= end_points.size();string name = end_points[index].name();cout << "选择了进程: " <<  name << " | 处理任务: " << command << endl;write(end_points[index]._w_fd, &command, sizeof(command));sleep(1);}
    }void waitProcess(const vector<EndPoint> &end_points)
    {for(int i = 0; i < end_points.size(); ++i){std::cout << "父进程让子进程退出:" << end_points[i]._c_id << std::endl;close(end_points[i]._w_fd);waitpid(end_points[i]._c_id, nullptr, 0);std::cout << "父进程回收了子进程:" << end_points[i]._c_id << std::endl;}
    }// #define A 0
    // #define B 1
    // #define C 2int main()
    {vector<EndPoint> end_points;createProcesses(&end_points);ctrlProcess(end_points);waitProcess(end_points);return 0;
    }

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

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

    相关文章

    centos-stream-9上安装nvidia驱动和cuda-toolkit

    这里写目录标题 驱动安装1. 更新系统2. NVIDIA GPU安装检查系统是否安装了 NVIDIA GPU2.1 首先&#xff0c;使用以下命令更新 DNF 软件包存储库缓存&#xff1a;2.2 安装编译 NVIDIA 内核模块所需的依赖项和构建工具2.3 在 CentOS Stream 9 上添加官方 NVIDIA CUDA 软件包存储库…

    LDAP高效数据同步:Syncrepl复制模式实战指南

    #作者&#xff1a;朱雷 文章目录 一、Syncrepl 复制简介1.1. 什么是复制模式1.2. 什么是 syncrepl同步复制 二、Ldap环境部署三、配置复制类型3.1. 提供者端配置3.2. 消费者端配置3.3.启动服务3.4.测试同步是否生效 四、总结 一、Syncrepl 复制简介 1.1. 什么是复制模式 Ope…

    Linux 内核网络协议栈中的 struct packet_type:以 ip_packet_type 为例

    在 Linux 内核的网络协议栈中,struct packet_type 是一个核心数据结构,用于注册特定协议类型的数据包处理逻辑。它定义了如何处理特定协议的数据包,并通过协议类型匹配机制实现协议分发。本文将通过分析 ip_packet_type 的定义和作用,深入探讨其在网络协议栈中的重要性。 …

    QT Sqlite数据库-教程001 创建数据库和表-下

    【1】创建带名称的数据库 #include <QtSql/QSqlDatabase> #include <QtSql/QSqlQuery> #include <QtSql/QSqlRecord> QString path QDir::currentPath(); QApplication::addLibraryPath(pathQString("/release/plugins")); QPluginLoader loader…

    Cannot find module ‘vue‘ or its corresponding type declarations

    在使用vue3vite创建新的工程时&#xff0c;在新增.vue文件时会出现Cannot find module vue这个错误。 只需要我们在项目中的.d.ts文件中添加以下代码即可 declare module *.vue {import { defineComponent } from vue;const component: ReturnType<typeof defineComponent&…

    SSRF打靶总结

    文章目录 一. PortSwigger1、本地服务器的基本SSRF2、基本的目标不是漏洞机3、Referer标头的外带SSRF4、简单黑名单的SSRF黑名单绕过思路&#xff1a; 5、重定向的SSRF6. 简单的白名单SSRF白名单绕过思路&#xff1a; 二、BWAPP1. SSRF 文件包含漏洞 | 内网探测2. XXE -> S…

    STL-函数对象

    1.函数对象 1.1 概念 重载函数调用操作符的类&#xff0c;其对象被称为函数对象 函数对象使用重载的&#xff08;&#xff09;时&#xff0c;行为类似函数调用&#xff0c;也成为仿函数 本质&#xff1a;函数对象&#xff08;仿函数&#xff09;是一个类&#xff0c;不是一…

    多线程(Java)

    注&#xff1a;本文为本人学习过程中的笔记 1.导入 1.进程和线程 我们希望我们的程序可以并发执行以提升效率&#xff0c;此时引入了多进程编程。可是创建进程等操作开销太大&#xff0c;于是就将进程进一步拆分成线程&#xff0c;减少开销。进程与进程之间所涉及到的资源是…

    在 Dev-C++中编译运行GUI 程序介绍(三)有趣示例一组

    在 Dev-C中编译运行GUI程序介绍&#xff08;三&#xff09;有趣示例一组 前期见 在 Dev-C中编译运行GUI 程序介绍&#xff08;一&#xff09;基础 https://blog.csdn.net/cnds123/article/details/147019078 在 Dev-C中编译运行GUI 程序介绍&#xff08;二&#xff09;示例&a…

    【高校主办】2025年第四届信息与通信工程国际会议(JCICE 2025)

    重要信息 会议网址&#xff1a;www.jcice.org 会议时间&#xff1a;2025年7月25-27日 召开地点&#xff1a;哈尔滨 截稿时间&#xff1a;2025年6月15日 录用通知&#xff1a;投稿后2周内 收录检索&#xff1a;EI,Scopus 会议简介 JCICE 2022、JCICE 2023、JCICE 2…

    【Linux】Linux 操作系统 - 03 ,初步指令结尾 + shell 理解

    文章目录 前言一、打包和压缩二、有关体系结构 (考)面试题 三、重要的热键四、shell 命令及运行原理初步理解五、本节命令总结总结 前言 本篇文章 , 笔者记录的笔记内容包含 : 基础指令 、重要热键 、shell 初步理解 、权限用户的部分问题 。 内容皆是重要知识点 , 需要认真理…

    Python: sqlite3.OperationalError: no such table: ***解析

    出现该错误说明数据库中没有成功创建 reviews 表。以下是完整的解决方案: 步骤 1:创建数据库表 在插入数据前,必须先执行建表语句。请通过以下任一方式创建表: 方式一:使用 SQLite 命令行 bash 复制 # 进入 SQLite 命令行 sqlite3 reviews.db# 执行建表语句 CREATE T…

    VSCode CLine 插件自定义配置使用 Claude 3.7 模型进行 AI 开发

    一个互联网技术玩家&#xff0c;一个爱聊技术的家伙。在工作和学习中不断思考&#xff0c;把这些思考总结出来&#xff0c;并分享&#xff0c;和大家一起交流进步。 本文介绍如何在 Visual Studio Code (VSCode) 中安装和自定义配置 CLine 插件&#xff0c;并使用 Claude 3.7 模…

    【VSCode配置】运行springboot项目和vue项目

    目录 安装VSCode安装软件安装插件VSCode配置user的全局设置setting.jsonworkshop的项目自定义设置setting.jsonworkshop的项目启动配置launch.json 安装VSCode 官网下载 安装软件 git安装1.1.12版本&#xff0c;1.2.X高版本无法安装node14以下版本 nvm安装&#xff08;github…

    linux shell编程之条件语句(二)

    目录 一. 条件测试操作 1. 文件测试 2. 整数值比较 3. 字符串比较 4. 逻辑测试 二. if 条件语句 1. if 语句的结构 (1) 单分支 if 语句 (2) 双分支 if 语句 (3) 多分支 if 语句 2. if 语句应用示例 (1) 单分支 if 语句应用 (2) 双分支 if 语句应用 (3) 多分支 …

    榕壹云在线商城系统:基于THinkPHP+ Mysql+UniApp全端适配、高效部署的电商解决方案

    项目背景&#xff1a;解决多端电商开发的痛点 随着移动互联网的普及和用户购物习惯的碎片化&#xff0c;传统电商系统面临以下挑战&#xff1a; 1. 多平台适配成本高&#xff1a;需要同时开发App、小程序、H5等多端应用&#xff0c;重复开发导致资源浪费。 2. 技术依赖第三方…

    神经动力学系统与计算及AI拓展

    大脑&#xff0c;一个蕴藏在我们颅骨之内的宇宙&#xff0c;以活动脉动&#xff0c;如同由电信号和化学信号编织而成的交响乐&#xff0c;精巧地协调着思想、情感和行为。但是&#xff0c;这种复杂的神经元舞蹈是如何产生我们丰富多彩的精神生活的呢&#xff1f;这正是神经动力…

    K8s常用基础管理命令(一)

    基础管理命令 基础命令kubectl get命令kubectl create命令kubectl apply命令kubectl delete命令kubectl describe命令kubectl explain命令kubectl run命令kubectl cp命令kubectl edit命令kubectl logs命令kubectl exec命令kubectl port-forward命令kubectl patch命令 集群管理命…

    本地化部署DeepSeek-R1蒸馏大模型:基于飞桨PaddleNLP 3.0的实战指南

    目录 一、飞桨框架3.0&#xff1a;大模型推理新范式的开启1.1 自动并行机制革新&#xff1a;解放多卡推理1.2 推理-训练统一设计&#xff1a;一套代码全流程复用 二、本地部署DeepSeek-R1-Distill-Llama-8B的实战流程2.1 机器环境说明2.2 模型与推理脚本准备2.3 启动 Docker 容…

    单片机方案开发 代写程序/烧录芯片 九齐/应广等 电动玩具 小家电 语音开发

    在电子产品设计中&#xff0c;单片机&#xff08;MCU&#xff09;无疑是最重要的组成部分之一。无论是消费电子、智能家居、工业控制&#xff0c;还是可穿戴设备&#xff0c;小家电等&#xff0c;单片机的应用无处不在。 单片机&#xff0c;简而言之&#xff0c;就是将计算机…