Linux 进程间通信之命名管道

💓博主CSDN主页:麻辣韭菜💓

⏩专栏分类:Linux知识分享⏪

🚚代码仓库:Linux代码练习🚚

🌹关注我🫵带你学习更多Linux知识
  🔝 

目录

前言

 命名管道

创建一个命名管道 

代码实现 

日志 

 可变参数列表

 获取时间的函数locatime

 snprintf函数

 snprintf函数



​ 


前言

书接上回,进程间通信我们利用管道可以通信,但是这些进程都是有血缘关系的进程,那有没有能让两个毫不相干的进程也能通信?有的,我们用命名管道,就能实现两个没有任何关系的进程进行通信。 

 命名管道

管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。
如果我们想在不相关的进程之间交换数据,可以使用 FIFO 文件来做这项工作,它经常被称为命名管道。
命名管道是一种特殊类型的文件

创建一个命名管道 

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

指令: mkfifo filename

 

echo本来是输出到显示器的,我们加入重定向后,重定到filename这个命名管道文件中,

cat输入重定向从filename当中读取数据 hello world ,我们再ll命令发现filename文件大小还是0。f

 

那如何删除命名管道啊?

指令:unlink filename 或者 rm -f filename

 

这时你会想知道,命令管道如何在程序中如何创建?代码实现我们先不急,先理解两个没有任何关系的进程,它们进程间通信的底层原理。

        从匿名管道中我们知道进程想要通信,首先就是要让两个进程看到同一份资源,匿名管道天然具有这种优势,子进程会继承父进程的所有资源。但是命名管道不是这样的,那如何让两个进程看到同一份资源? 

        那就是两个进程打开同一个文件。那如何确保两个进程打开的是同一份文件?首先从OS层面来说,我管你是进程A还是进程B又或者进程C,你们三打开同一个文件,看似打开了三次,实际上OS为了效率,它只会打开一次这个文件,既然这样,那两个进程要通信,直接打开文件名相同的且路径相同文件就行了。为什么加路径?因为路径是唯一的。所以我们就能确保两个进程打开的是同一份文件。

这时有人就会问了,那我直接在两个进程中用系统调用open这个函数打开文件不就看到同一份资源了吗?一个进行读,一个进行写不就通信了吗?为什么还要用命名管道。首先两个进程从磁盘上读取,加载到内存,又从内存中写入,拷贝到磁盘中,这个过程就很慢,凡是和磁盘打交道,那就是慢慢慢!!!而刚才我们看到filename是没有字节的,这就意味着命名管道是不需要刷盘的,它是内存级的文件速度很快。 

代码实现 

实现之前我们需要先了解一个系统调用接口 mkfifo

mkfifo 函数用于创建一个 FIFO (First-In-First-Out)特殊文件,也就是一个命名管道。以下是 mkfifo 函数的手册页翻译:

名称 mkfifo - 创建一个 FIFO 特殊文件(命名管道)

头文件

            #include <sys/types.h>

            #include <sys/stat.h>

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

描述 mkfifo() 用指定的 pathname 创建一个 FIFO 特殊文件。mode 参数指定了 FIFO 的权限。文件的权限会受进程的掩码(umask)影响:创建的文件权限为 (mode & ~umask)。

   FIFO 特殊文件类似于管道,但是创建方式不同。FIFO 特殊文件会通过调用 mkfifo() 进入文件系统。一旦通过这种方式创建了一个 FIFO 特殊文件,任何进程都可以像操作普通文件一样打开它进行读写。但是,在进行任何输入输出操作之前,必须同时在两端打开它。尝试打开 FIFO 进行读取通常会阻塞,直到有其他进程打开相同的 FIFO 进行写入,反之亦然。参见 fifo(7) 中关于非阻塞处理 FIFO 特殊文件的内容。

返回值 mkfifo() 成功时返回 0。失败时返回 -1(此时 errno 被适当地设置)。

错误 EACCES pathname 中的一个目录不允许搜索(执行)权限。

   EDQUOT 用户在文件系统上的磁盘块或索引节点配额已经耗尽。EEXIST pathname 已经存在。这包括 pathname 是符号链接(悬空或非悬空)的情况。ENAMETOOLONG要么 pathname 的总长度超过了 PATH_MAX,要么其中一个文件名组件的长度超过了 NAME_MAX。在 GNU 系统中,文件名长度没有强制限制,但一些文件系统可能对文件名组件的长度施加限制。ENOENT pathname 中的一个目录组件不存在或者是一个悬空的符号链接。ENOSPC 目录或文件系统没有足够的空间用来创建新文件。ENOTDIRpathname 中的一个组件用作目录,但事实上不是目录。EROFS  pathname 指向的是一个只读文件系统。
#pragma once#include <sys/types.h>
#include <sys/stat.h>#define FIFO_FILE  "./myFileName"

 我们先自定义头文件comm.hpp这个头文件,我们在这里头文件创建命名管道 client.cc和server.cc这个两个源文件同时包含。就能看到同一个文件->命名管道。

创建命名管道 

#pragma once
#include <iostream>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/stat.h>#define FIFO_FILE  "./myFileName"
#define MODE 0664//退出码
enum
{FIFO_CREATE_ERR = 1  //管道创建失败退出码设置为1};
#include "comm.hpp"
using namespace std;
// 创建管道
int main ()
{int n = mkfifo(FIFO_FILE,MODE);if(n == -1){perror("mkfifo");exit(FIFO_CREATE_ERR);}return 0;
}

 我们用sever来试试能不能创建创建管道。

 

管道创建好了是不是就能进行通信了? 当然不行,我们现在只是让它们看到同一份资源。

那如何通信?别忘了linux下一切皆文件,当然我们的命名也是文件,那我们就可以利用之前的文件操作的那一套进行通信。open read write close 就是这么的简单。

下面我让client写,server读。 

#include "comm.hpp"
using namespace std;
int main ()
{int fd = open(FIFO_FILE,O_WRONLY);if(fd < 0){perror("open");exit(FIFO_OPEN_ERR);}//客户端写入string line;while(true){cout << "Please Enter@ ";getline(cin , line);int x = write(fd,line.c_str(),line.size());}close(fd);return 0;
}

#include "comm.hpp"
using namespace std;
// 创建管道
int main ()
{//创建管道int n = mkfifo(FIFO_FILE, MODE);if (n == -1){perror("mkfifo");exit(FIFO_CREATE_ERR);}//打开信道int fd = open(FIFO_FILE,O_RDONLY);if(fd < 0){perror("open");exit(FIFO_OPEN_ERR);}//开始通信while(true){char buffer[1024] = {0};int x = read(fd,buffer,sizeof(buffer));if(x > 0){buffer[x] = 0;cout << "client say# " << buffer << endl;}}close(fd);return 0;
}

 

 前面server写的创建管道不好,每次在命令端都要手动删除管道。下面进行优化,我们comm这个头文件中写一个创建管道的类 构造函数初始化管道,析构函数删除管道

class Init
{
public:Init(){int n = mkfifo(FIFO_FILE, MODE);if (n == -1){perror("mkfifo");exit(FIFO_CREATE_ERR);}}~Init(){int m = unlink(FIFO_FILE);if(m == -1){perror("unlink");exit(FIFO_DELETE_ERR);}}
};

其实命名管道的代码比匿名管道代码实现要简单的多。作为一名程序员,你写的程序肯定是要报错的,有时候也会其他的情况,告警等等。那有没有一种方法可以知道我们的程序在哪里出错了,什么时间出错的?出错的原因是什么?并把这些信息写入一个文件中方便我们查看。有的 日志

日志 

 可变参数列表

关于打印日志需要用到可变参数列表那什么是可变参数列表? 

可变参数列表是指函数在定义时允许接受不定数量的参数。在许多编程语言中,包括Python,Java和C++等,都支持可变参数列表的特性。

#include <stdarg.h>
#include <stdio.h>// 一个接受可变参数列表的函数
void print_arguments(int num, ...) {va_list args;  // 定义一个va_list类型的变量va_start(args, num);  // 初始化args,指向第一个可变参数for (int i = 0; i < num; i++) {int value = va_arg(args, int);  // 获取下一个可变参数的值printf("%d ", value);}va_end(args);  // 结束args的使用printf("\n");
}int main() {print_arguments(3, 10, 20, 30);// Output: 10 20 30return 0;
}

 获取时间的函数locatime

localtime函数是C和C++标准库time.h中的一个函数,用于将日历时间(秒数)转换为本地时间的struct tm结构体。struct tm结构体表示了时间的各个部分,如年、月、日、小时、分钟和秒等。

以下是localtime函数的声明和简单示例:

struct tm *localtime(const time_t *timer);
  • timer:指向要转换的日历时间的指针。

示例:

#include <time.h>
#include <stdio.h>int main() {time_t current_time;struct tm *local_time;time(&current_time);local_time = localtime(&current_time);printf("Current local time: %d-%d-%d %d:%d:%d\n", local_time->tm_year + 1900, local_time->tm_mon + 1, local_time->tm_mday,local_time->tm_hour, local_time->tm_min, local_time->tm_sec);return 0;
}

 snprintf函数

snprintf函数是C语言中的一个格式化字符串输出函数,它可以控制输出字符的数量,避免缓冲区溢出的风险。该函数的作用类似于printf,但是它可以指定输出的最大长度。

以下是snprintf函数的声明和简单示例:

int snprintf(char *str, size_t size, const char *format, ...);
  • str:指向用于存储输出的字符数组的指针。
  • size:输出的字符数目(包括结尾的空字符)。
  • format:格式化字符串,用来指定输出的格式。
  • ...:可变数量的参数,根据format字符串的具体内容而定。

示例:

#include <stdio.h>int main() {char buffer[50];int num = 123;snprintf(buffer, 50, "The number is %d\n", num);printf("Output: %s", buffer);return 0;
}

 vsnprintf函数

snprintf函数将格式化后的字符串输出到buffer数组中,最多输出50个字符。即使格式化后的字符串超过了50个字符,snprintf也会在50个字符后停止输出,避免了缓冲区溢出的问题。

通过使用snprintf可以更加安全地进行字符串格式化输出,特别是在处理用户输入等可能带有风险的数据时,可以有效防止缓冲区溢出漏洞。

vsnprintf函数是C语言中的一个格式化字符串输出函数,它类似于snprintf,允许用户指定输出字符的最大数量。与snprintf不同的是,vsnprintf函数使用一个va_list类型的参数来传递可变数量的参数。

以下是vsnprintf函数的声明和简单示例:

#include <stdio.h>
#include <stdarg.h>int vsnprintf(char *str, size_t size, const char *format, va_list ap);
  • str:指向用于存储输出的字符数组的指针。
  • size:输出的字符数目(包括结尾的空字符)。
  • format:格式化字符串,用来指定输出的格式。
  • apva_list类型的参数列表。

示例:

#include <stdio.h>
#include <stdarg.h>void format_string(char *buffer, size_t size, const char *format, ...) {va_list args;va_start(args, format);vsnprintf(buffer, size, format, args);va_end(args);
}int main() {char buffer[50];format_string(buffer, 50, "The number is %d\n", 123);printf("Output: %s", buffer);return 0;
}

我们定义了一个辅助函数format_string,它使用vsnprintf来格式化字符串并输出到buffer数组中。通过使用va_list类型的参数列表,vsnprintf可以接受可变数量的参数,更加灵活地进行字符串格式化输出。

vsnprintf函数通常用于创建可变参数的格式化字符串输出函数,可以在实现自定义格式化输出时使用,以提供更加灵活和安全的字符串处理能力。

有了这些基础我们就可以写日志了

​
#pragma once#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>#define SIZE 1024#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4#define Screen 1
#define Onefile 2
#define Classfile 3#define LogFile "log.txt"class Log
{
public:Log(){printMethod = Screen;path = "./log/";}void Enable(int method){printMethod = method;}std::string levelToString(int level){switch (level){case Info:return "Info";case Debug:return "Debug";case Warning:return "Warning";case Error:return "Error";case Fatal:return "Fatal";default:return "None";}}void printLog(int level, const std::string &logtxt){switch (printMethod){case Screen:std::cout << logtxt << std::endl;break;case Onefile:printOneFile(LogFile, logtxt);break;case Classfile:printClassFile(level, logtxt);break;default:break;}}void printOneFile(const std::string &logname, const std::string &logtxt){std::string _logname = path + logname;int fd = open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666); // "log.txt"if (fd < 0)return;write(fd, logtxt.c_str(), logtxt.size());close(fd);}void printClassFile(int level, const std::string &logtxt){std::string filename = LogFile;filename += ".";filename += levelToString(level); // "log.txt.Debug/Warning/Fatal"printOneFile(filename, logtxt);}~Log(){}void operator()(int level, const char *format, ...){time_t t = time(nullptr);struct tm *ctime = localtime(&t);char leftbuffer[SIZE];snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,ctime->tm_hour, ctime->tm_min, ctime->tm_sec);va_list s;va_start(s, format);char rightbuffer[SIZE];vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);va_end(s);// 格式:默认部分+自定义部分char logtxt[SIZE * 2];snprintf(logtxt, sizeof(logtxt), "%s %s\n", leftbuffer, rightbuffer);// printf("%s", logtxt); // 暂时打印printLog(level, logtxt);}private:int printMethod;std::string path;
};​

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

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

相关文章

Leetcode—976. 三角形的最大周长【简单】(ranges::sort函数)

2024每日刷题&#xff08;122&#xff09; Leetcode—976. 三角形的最大周长 实现代码 class Solution { public:int largestPerimeter(vector<int>& nums) {ranges::sort(nums);for(int i nums.size() - 1; i > 1; i--) {if(nums[i - 1] nums[i - 2] > nu…

洛谷 P1377 [TJOI2011]:树的序 ← 笛卡尔树

【题目来源】https://www.luogu.com.cn/problem/P1377【题目描述】 众所周知&#xff0c;二叉查找树的形态和键值的插入顺序密切相关。准确的讲&#xff1a; 1.空树中加入一个键值 k&#xff0c;则变为只有一个结点的二叉查找树&#xff0c;此结点的键值即为 k。 2.在非空树中插…

智能物联网与Web3:连接未来数字生活的桥梁

随着科技的不断进步&#xff0c;智能物联网&#xff08;IoT&#xff09;和Web3技术正成为数字化时代的关键驱动力。智能物联网将各种物理设备连接到互联网&#xff0c;使其能够感知环境、收集数据并与其他设备通信&#xff0c;而Web3技术则以去中心化、安全性和透明性为核心&am…

Linux开发板 FTP 服务器移植与搭建

VSFTPD&#xff08;Very Secure FTP Daemon&#xff09;是一个安全、稳定且快速的FTP服务器软件&#xff0c;广泛用于Unix和Linux操作系统。它以其轻量级、高效和易于配置而受到赞誉。VSFTPD不仅支持标准的FTP命令和操作&#xff0c;还提供了额外的安全特性&#xff0c;如匿名F…

Python日志记录库之logbook使用详解

概要 在软件开发和运维中,日志记录是一项至关重要的任务。Python 的 Logbook 库是一个强大而灵活的日志记录工具,提供了丰富的功能和易用的接口。本文将深入探讨 Logbook 库的特性、用法,并通过丰富的示例代码展示其在实际项目中的应用。 Logbook 简介 Logbook 是一个为 P…

本地搭建llama大模型及对话UI

环境说明&#xff1a;MBP 2023 M2Pro芯片 用到的工具/组件/技术&#xff1a;ollama、llama3:8b、docker、open-webui 1.下载ollama ollama官网下载地址&#xff1a;https://ollama.com/download 到ollama官网地址下载对应操作系统版本的ollama平台&#xff0c;按照安装指引…

springboot 获取maven打包时间

springboot 获取maven打包时间 pom <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.13.RELEASE</version><relativePath /> <!-- lookup parent…

民航电子数据库:mysql与cae(insert语法差异)

目录 示例1、cae插入数据时不支持value关键字&#xff0c;只能使用values2、insert时&#xff0c;就算是自增主键&#xff0c;只要新增时包含了主键&#xff0c;该主键就必须有值&#xff0c;否则会报错&#xff1a;字段xxx不能取空值 对接民航电子数据库&#xff0c;本篇记录i…

用栈实现队列——leetcode刷题

题目要求我们只用栈的基本操作 push to top 入栈&#xff0c;peek from top 返回栈顶元素&#xff0c;pop from top 移除并返回栈顶元素&#xff0c;size 栈的大小&#xff0c;is_empty 判断栈是否为空&#xff0c;这几个函数来实现队列&#xff0c;也就是说&#xff0c;我们在…

salesforce 如何访问lwc组件

访问lwc有哪些途径呢? Action ButtonTabAura use lwc(拓展)如何区分是新建页面还是编辑页面 Action Button xml文件中要配置tab<?xml version"1.0" encoding"UTF-8"?> <LightningComponentBundle xmlns"http://soap.sforce.com/2006/04/…

全景剖析阿里云容器网络数据链路(七):Terway DataPath V2(Terway≥1.8.0)

作者&#xff1a;余凯 前言 近几年&#xff0c;企业基础设施云原生化的趋势越来越强烈&#xff0c;从最开始的IaaS化到现在的微服务化&#xff0c;客户的颗粒度精细化和可观测性的需求更加强烈。容器网络为了满足客户更高性能和更高的密度&#xff0c;也一直在高速的发展和演…

Docker 简单使用及安装常用软件

一、Docker 安装、配置与卸载 1.1、Docker 安装 # 1.安装gcc环境 yum -y install gcc gcc-c && \# 2. 卸载docker旧版本&#xff08;可能之前有安装&#xff09; yum -y remove docker docker-common docker-selinux docker-engine && \# 3. 安装依赖的软件包…

PHP源码_最新在线工具箱网站系统源码

项目运行截图 源码贡献 https://githubs.xyz/boot?app41 部分数据库表 SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS 0;-- ---------------------------- -- Table structure for toolbox_category -- ---------------------------- DROP TABLE IF EXISTS toolbox_category…

Golang | Leetcode Golang题解之第64题最小路径和

题目&#xff1a; 题解&#xff1a; func minPathSum(grid [][]int) int {if len(grid) 0 || len(grid[0]) 0 {return 0}rows, columns : len(grid), len(grid[0])dp : make([][]int, rows)for i : 0; i < len(dp); i {dp[i] make([]int, columns)}dp[0][0] grid[0][0]…

ubuntu sudo apt-get install neo4j 配置安装与设置远程访问

文章目录 下载Adding the Debian repositoryInstalling Neo4j安装流程设置远程访问 下载 neo4j 官方的下载地址&#xff0c;进入页面之后&#xff0c;往下滑&#xff1a; https://neo4j.com/deployment-center/#community 点击 Visit https://debian.neo4j.com/ Adding the …

Apache Seata基于改良版雪花算法的分布式UUID生成器分析1

title: Seata基于改良版雪花算法的分布式UUID生成器分析 author: selfishlover keywords: [Seata, snowflake, UUID] date: 2021/05/08 本文来自 Apache Seata官方文档&#xff0c;欢迎访问官网&#xff0c;查看更多深度文章。 Seata基于改良版雪花算法的分布式UUID生成器分析…

电子式汽车机油压力传感器的接线方法及特点

电子式机油压力传感器由厚膜压力传感器芯片、信号处理电路、外壳、固定电路板装置和两根引线&#xff08;信号线和报警线&#xff09;组成。信号处理电路由电源电路、传感器补偿电路、调零电路、电压放大电路、电流放大电路、滤波电路和报警电路组成。 厚膜压力传感器是20世纪…

数据结构 - C/C++

快速跳转 数组链表栈队列串树 目录 数据结构 逻辑结构 物理结构 数据结构 数据 数据不仅仅包括整型、实型等数值类型&#xff0c;还包括字符及声音、图像、视频等非数值类型。 计算机可以理解并按照指定格式处理。 结构 元素相互之间存在一种或多种特定关系的数据集合。 …

tuxera ntfs for mac是什么 tuxera ntfs for mac怎么用 tuxera激活码

Tuxera NTFS for Mac是一款完全的mac读写软件&#xff0c;可辅助mac电脑读写ntfs格式&#xff0c;进行磁盘的管理。本文会详细讲解tuxera ntfs for mac的操作方法。 Tuxera NTFS for Mac 2023安装包免费下载&#xff1a;https://souurl.cn/IE35lO 一、Tuxera NTFS for Mac是什…

goget配置多个golang 运行环境

一台主机安装多个golang 运行环境 本环境 windows10 为 基础 mac linux也可以按照此方法操作 背景 开发不同的运维工具会用到不同版本的golang&#xff0c;但是开发者不能一直进行重装来处理 &#xff0c;因此 需要一个工具进行golang版本的管理 go管理工具介绍 gvm (Go V…