在虚拟宇宙中低语——进程间通信,Linux命名管道的前世今生
文章目录
- 🌌 序章
- 🌠 一、命名管道的宿命与哲学
- 1.1、创建及简单使用
- 1.2、命名管道的工作原理
- 1.3、命名管道与匿名管道的区别
- 2、命名管道的特点及特殊场景
- 2.1、特点
- 2.2、四种特殊场景
- 3、命名管道实操
- 3.1、实现文件拷贝
- 3.2、实现进程控制
- 小结

🌌 序章
在操作系统的寂静星辰之间,进程如宇宙中的恒星各自运转,孤独却不懈。而在某一刻,它们渴望对话,渴望将彼此的思想穿越内存的风暴,投递至彼此心中。于是,
进程间通信(IPC)
如星际信使般诞生。而在众多信使中,有一种古老而优雅的方式被称为:命名管道
(Named Pipe,亦称FIFO)。
这便是我们今天的主角——在系统的寂静深处低语的“命名管道”。
🌠 一、命名管道的宿命与哲学
命名管道
,是一种半双工
通信机制,允许两个不具亲缘关系的进程通过内核中一个具名的管道文件进行通信。
正如信使需要地址,命名管道也需栖身于文件系统中,等待两端的进程将心语倾诉或倾听。
与匿名管道(pipe)
不同,命名管道拥有一个路径名
,存在于文件系统中,通常通过命令 mkfifo
或系统调用mkfifo()
创建。即便通信双方毫无血缘(非父子进程),只要知晓这路径,便能互通心声。
那么如何给 匿名管道
起名字呢?
结合文件系统,给匿名管道这个纯纯的内存文件分配 inode
,将文件名与之构建联系,关键点在于不给它分配 Data block
,因为它是一个纯纯的内存文件,是不需要将数据刷盘到磁盘中的
可以将命名管道理解为 “挂名” 后的匿名管道,把匿名管道加入文件系统中,但仅仅是挂个名而已,目的就是为了让其他进程也能看到这个文件(文件系统中的文件可以被所有进程看到)
因为没有 Data block
,所以命名管道这个特殊文件大小为 0
1.1、创建及简单使用
命令管道的创建依赖于函数 mkfifo
,函数原型如下
#include <sys/types.h>
#include <sys/stat.h>int mkfifo(const char *pathname, mode_t mode);
关于 mkfifo
函数
-
对于参数1,既可以传递绝对路径
/home/xxx/namePipeCode/fifo
,也可以传递相对路径./fifo
,当然绝对路径更灵活,但也更长 -
对于参数2,
mode_t
其实就是对unsigned int
的封装,等价于uint32_t
,而mode
就是创建命名管道时的初始权限,实际权限需要经过umask
掩码计算
不难发现,mkfifo
和 mkdir
非常像,其实 mkfifo
可以直接在命令行中运行
创建一个名为fifo
的命名管道文件
mkfifo fifo
p是管道文件,大小为0
这个管道文件也非常特殊:大小为 0
,从侧面说明 管道文件就是一个纯纯的内存级文件
,有自己的上限,出现在文件系统中,只是单纯挂个名而已
可以直接在命令行中使用命名管道:
echo
可以进行数据写入,可以重定向至fifo
cat
可以进行数据读取,同样也可以重定向于fifo
打开两个终端窗口(两个进程),即可进行通信
命名管道遵循管道的四种场景,因此在写端未写入数据时,读端会阻塞
当然也可以通过程序实现两个独立进程 IPC
思路:创建 服务端 server
和 客户端 client
两个独立的进程,服务端 server
创建并以 读
的方式打开管道文件,客户端 client
以 写
的方式打开管道文件,打开后俩进程可以进程通信,通信结束后,由客户端关闭 写端
(服务端 读端
读取到 0 后也关闭并删除命令管道文件)
注意:
- 当管道文件不存在时,文件会打开失败,因此为了确保正常通信,需要先运行服务端
server
创建管道文件 - 服务端启动后,因为是读端,所以会阻塞等待
客户端
(写端)写入数据 - 客户端写入数据时,因为 ‘\n’ 也被读取了,所以要去除此字符
- 通信结束后,需要服务端主动删除管道文件
unlink 命令管道文件名 //删除管道文件
为了让服务端和客户端能享有同一个文件名,可以创建一个公共头文件 common.h,其中存储 命名管道文件名及默认权限等公有信息
公共资源
common.h
#pragma once#include <iostream>
#include <string>std::string fifo_name = "./fifo"; //管道名
uint32_t mode = 0666; //权限
服务端(写端) server.cc 提供文件拷贝服务
#include <iostream>
#include <cstring>
#include <cerrno>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "common.h"using namespace std;int main()
{// 服务端// 1、打开文件int wfd = open(fifo_name.c_str(), O_WRONLY);if (wfd < 0){cerr << "open fail! errno: " << errno << " | " << strerror(errno) << endl;exit(0);}// 2、打开源文件FILE *fp = fopen("file.txt", "r");if (fp == NULL){cerr << "fopen fail! errno: " << errno << " | " << strerror(errno) << endl;exit(0);}// 3、读取源文件数据char buff[1024];int n = fread(buff, sizeof(char), sizeof(buff), fp);//IPC区域// 4、写入源文件至命名管道write(wfd, buff, strlen(buff));cout << "服务端已向管道写入: " << n << "字节的数据" << endl;//IPC区域fclose(fp);fp = nullptr;close(wfd);return 0;
}
客户端(读端) client.cc 从服务端中拷贝文件(下载)
#include <iostream>
#include <cstring>
#include <cerrno>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "common.h"using namespace std;int main()
{// 客户端// 1、创建命名管道文件int ret = mkfifo(fifo_name.c_str(), mode);if (ret < 0){cerr << "mkfifo fail! errno: " << errno << " | " << strerror(errno) << endl;exit(0);}// 2、以读的方式打开管道文件int rfd = open(fifo_name.c_str(), O_RDONLY);if (rfd < 0){cerr << "open fail! errno: " << errno << " | " << strerror(errno) << endl;exit(0);}// 3、打开目标文件FILE *fp = fopen("target.txt", "w");if (fp == NULL){cerr << "fopen fail! errno: " << errno << " | " << strerror(errno) << endl;exit(0);}//IPC区域// 4、读取数据char buff[1024];int n = read(rfd, buff, sizeof(buff) - 1);buff[n] = '\0';if (n > 0)cout << "客户端已从管道读取: " << n << "字节的数据" << endl;else if (n == 0)cout << "写端关闭,读端读取到0,终止读端" << endl;elsecout << "读取异常" << endl;//IPC区域//5、写入目标文件,完成拷贝fwrite(buff, sizeof(char), strlen(buff), fp);cout << "客户端已成功从服务端下载(拷贝)了文件数据" << endl;fclose(fp);close(rfd);unlink(fifo_name.c_str()); // 删除命名管道文件return 0;
}
注:strcasecmp
是一个字符串比较函数,无论字符串大小写,都能进行比较
所以 挂了名之后的命名管道是如何实现独立进程间 IPC 的呢?
1.2、命名管道的工作原理
把视角拉回文件系统:当重复多次打开同一个文件时,并不会费力的打开多次,而且在第一次打开的基础上,对struct file
结构体中的引用计数 ++,所以对于同一个文件,不同进程打开了,看到的就是同一个
具体例子:显示器文件(stdout)
只有一个吧,是不是所有进程都可以同时进行写入?
同理,命名管道文件也是如此,先创建出文件,在文件系统中挂个名,然后让独立的进程以不同的方式打开同一个命名管道文件,比如进程 A 以只读的方式打开,进程 B 以只写的方式打开,那么此时进程 B 就可以向进程 A 写文件,即 IPC
因为命名管道适用于独立的进程间 IPC,所以无论是读端和写端,进程 A、进程 B 为其分配的 fd
是一致的,都是 3
如果是匿名管道
,因为是依靠继承才看到同一文件的,所以读端和写端fd
不一样
所以 命名管道
和 匿名管道
还是有区别的
1.3、命名管道与匿名管道的区别
不同点:
- 匿名管道只能用于具有血缘关系的进程间通信;而命名管道不存在该限制,谁都可以用
- 匿名管道直接通过
pipe
函数创建使用;而命名管道需要先通过mkfifo
函数创建,然后再通过open
打开使用 - 出现多条匿名管道时,可能会出现写端 fd 重复继承的情况;而命名管道不会出现这种情况
- 在其他方面,匿名管道与命名管道几乎一致
两个都属于管道家族,都是最古老的进程间通信方式,都自带同步与互斥机制,提供的都是流式数据传输
2、命名管道的特点及特殊场景
命名管道的特点及特殊场景与匿名管道完全一致,这里简单回顾下,详细内容可跳转至 《Linux进程间通信【匿名管道】》
2.1、特点
可以简单总结为:
- 管道是半双工通信
- 管道生命随进程而终止
- 命名管道任意多个进程间通信
- 管道提供的是流式数据传输服务
- 管道自带
同步与互斥
机制
2.2、四种特殊场景
四种场景分别为
- 管道为
空
时,读端阻塞,等待写端写入数据 - 管道为
满
时,写端阻塞,等待读端读取数据 - 进程通信时,关闭读端,OS 发出
13 号信号 SIGPIPE
终止写端进程 - 进程通信时,关闭写端,读端读取到
0
字节数据,可以借此判断终止读端
3、命名管道实操
以下是一些使用命名管道实现的简单小程序,主要目的是为了熟悉命名管道的使用
3.1、实现文件拷贝
下载应用的本质是在下载文件,将服务器看作写端,自己的电脑看作读端,那么下载
这个动作本质上就是 IPC
,不过是在网络层面实现的
我们可以利用 命名管道
实现不同进程间 IPC,即进程从文件中读取并写入一批数据,另一个进程一次读取一批数据并保存至新文件中,这样就实现了文件的拷贝
目标:利用命名管道,向空文件 target.txt
中写入数据,即拷贝
源文件 file.txt
公共资源
common.h
#pragma once#include <iostream>
#include <string>std::string fifo_name = "./fifo"; //管道名
uint32_t mode = 0666; //权限
服务端(写端)
server.cc
提供文件拷贝服务
#include <iostream>
#include <cstring>
#include <cerrno>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "common.h"using namespace std;int main()
{// 服务端// 1、打开文件int wfd = open(fifo_name.c_str(), O_WRONLY);if (wfd < 0){cerr << "open fail! errno: " << errno << " | " << strerror(errno) << endl;exit(0);}// 2、打开源文件FILE *fp = fopen("file.txt", "r");if (fp == NULL){cerr << "fopen fail! errno: " << errno << " | " << strerror(errno) << endl;exit(0);}// 3、读取源文件数据char buff[1024];int n = fread(buff, sizeof(char), sizeof(buff), fp);//IPC区域// 4、写入源文件至命名管道write(wfd, buff, strlen(buff));cout << "服务端已向管道写入: " << n << "字节的数据" << endl;//IPC区域fclose(fp);fp = nullptr;close(wfd);return 0;
}
客户端(读端)
client.cc
从服务端中拷贝文件(下载)
#include <iostream>
#include <cstring>
#include <cerrno>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "common.h"using namespace std;int main()
{// 客户端// 1、创建命名管道文件int ret = mkfifo(fifo_name.c_str(), mode);if (ret < 0){cerr << "mkfifo fail! errno: " << errno << " | " << strerror(errno) << endl;exit(0);}// 2、以读的方式打开管道文件int rfd = open(fifo_name.c_str(), O_RDONLY);if (rfd < 0){cerr << "open fail! errno: " << errno << " | " << strerror(errno) << endl;exit(0);}// 3、打开目标文件FILE *fp = fopen("target.txt", "w");if (fp == NULL){cerr << "fopen fail! errno: " << errno << " | " << strerror(errno) << endl;exit(0);}//IPC区域// 4、读取数据char buff[1024];int n = read(rfd, buff, sizeof(buff) - 1);buff[n] = '\0';if (n > 0)cout << "客户端已从管道读取: " << n << "字节的数据" << endl;else if (n == 0)cout << "写端关闭,读端读取到0,终止读端" << endl;elsecout << "读取异常" << endl;//IPC区域//5、写入目标文件,完成拷贝fwrite(buff, sizeof(char), strlen(buff), fp);cout << "客户端已成功从服务端下载(拷贝)了文件数据" << endl;fclose(fp);close(rfd);unlink(fifo_name.c_str()); // 删除命名管道文件return 0;
}
拷贝结果:成功拷贝
此时 服务端是写端,客户端是读端
,实现的是 下载
服务;当 服务端是读端,客户端是写端时
,实现的就是 上传
服务,搞两条管道就能模拟实现简单的 数据双向传输
服务
注意: 创建管道文件后,无论先启动读端,还是先启动写端,都要阻塞式的等待另一方进行交互
3.2、实现进程控制
在 Linux 匿名管道 IPC
中,我们实现了一个简易版的进程控制程序,原理是通过多条匿名管道实现父进程对多个子进程执行任务分配
匿名管道用于有血缘关系间 IPC,命名管道也可以
所以我们可以把上一篇文章中的 匿名管道
换为命名管道,一样可以实现通信
任务池 Task.hpp
#include <iostream>
#include <string>
#include <functional>
#include <unordered_map>
#include <unistd.h>using namespace std;void PrintLOG()
{cout << "PID: " << getpid() << " 正在执行打印日志的任务…" << endl;
}void InsertSQL()
{cout << "PID: " << getpid() << " 正在执行数据库插入的任务…" << endl;
}void NetRequst()
{cout << "PID: " << getpid() << " 正在执行网络请求的任务…" << endl;
}class Task
{
public:Task(){// 装载任务_tt = {{"打印日志", PrintLOG}, {"数据库插入", InsertSQL}, {"网络请求", NetRequst}};}// 展示任务void showTask(){cout << "目前可用任务有:[";for (auto e : _tt)cout << e.first << " ";cout << "]" << endl;cout << "输入 退出 以终止程序" << endl;}// 执行任务void Execute(const string &task){if (_tt.count(task) == 0){cerr << "没有这个任务:" << task << endl;}else{_tt[task](); // 函数对象调用}}private:unordered_map<string, function<void(void)>> _tt;
};
控制程序
namePipeCtrl.cc
包括进程、管道创建,任务执行与进程等待
#include <iostream>
#include <string>
#include <vector>
#include <cassert>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "Task.hpp"using namespace std;enum
{NAME_SIZE = 64
};// 子进程基本信息类
class ProcINfo
{public:ProcINfo(pid_t pid = pid_t(), int wfd = int()): _pid(pid), _wfd(wfd), _num(_cnt++){char buff[NAME_SIZE] = {0};snprintf(buff, NAME_SIZE, "Process %d | pid:wfd [%d:%d]", _num, _pid, _wfd);_name = string(buff);}pid_t _pid;int _wfd;int _num;string _name;static int _cnt;
};int ProcINfo::_cnt = 0;// 进程控制类
class ProcCtrl
{
public:ProcCtrl(int num = 3, mode_t mode = 0666): _num(num), _mode(mode){// 根据 _num 创建命名管道及子进程CreatPipeAndProc();}~ProcCtrl(){waitProc();}// 创建管道及进程void CreatPipeAndProc(){// 因为是继承的,所以也要注意写端重复继承问题vector<int> fds;for (int i = 0; i < _num; i++){// 步骤:创建管道,存入 _vstchar pipeNameBUff[NAME_SIZE]; // 管道名缓冲区snprintf(pipeNameBUff, NAME_SIZE, "./fifo-%d", i);int ret = mkfifo(pipeNameBUff, _mode);assert(ret != -1);(void)ret;_vst.push_back(string(pipeNameBUff));// 创建子进程,让子进程以只读的方式打开管道文件pid_t id = fork();if (id == 0){// 子进程内// 先关闭不必要的写端for (auto e : fds)close(e);// 打开管道文件,并进入任务等待默认(读端阻塞)int rfd = open(_vst[i].c_str(), O_RDONLY);assert(rfd != -1);(void)rfd;waitCommand(rfd);close(rfd); // 关闭读端exit(0);}// 父进程以写打开管道,保存 fd 信息int wfd = open(_vst[i].c_str(), O_WRONLY);assert(wfd != -1);(void)wfd;// 注册子进程信息_vpt.push_back(ProcINfo(id, wfd));fds.push_back(wfd);}}// 子进程等待任务派发void waitCommand(int rfd){while (true){char buff[NAME_SIZE] = {0};int n = read(rfd, buff, sizeof(buff) - 1);buff[n] = '\0';if (n > 0){Task().Execute(string(buff));}else if (n == 0){cerr << "读端读取到 0,写端已关闭,读端也即将关闭" << endl;break;}else{cerr << "子进程读取异常!" << endl;break;}}}// 展示可选进程void showProc(){cout << "目前可用进程有:[";int i = 0;for (i = 0; i < _num - 1; i++)cout << i << "|";cout << i << "]" << endl;}// 下达任务给子进程void ctrlProc(){while (true){cout << "==========================" << endl;int n = 0;do{showProc();cout << "请选择子进程:> ";cin >> n;} while (n < 0 || n >= _num);Task().showTask();string taskName;cout << "请选择任务:> ";cin >> taskName;if (taskName == "退出")break;// 将信息通过命名管道写给子进程cout << "选择进程 ->" << _vpt[n]._name << " 执行 " << taskName << " 任务" << endl;write(_vpt[n]._wfd, taskName.c_str(), taskName.size());sleep(1);}}// 关闭写端、删除文件、等待子进程退出void waitProc(){for (int i = 0; i < _num; i++){close(_vpt[i]._wfd); // 关闭写端unlink(_vst[i].c_str()); // 关闭管道文件waitpid(_vpt[i]._pid, nullptr, 0); // 等待子进程}cout << "所有子进程已回收" << endl;}private:vector<ProcINfo> _vpt; // 子进程信息表vector<string> _vst; // 命名管道信息表int _num; // 子进程数/命名管道数mode_t _mode; // 命名管道文件的权限
};int main()
{ProcCtrl p1;p1.ctrlProc();return 0;
}
关于 父子进程间使用命名管道通信
值得注意的问题:
- 在命名管道创建后,需要先创建子进程,让子进程打开
读端或写端
,然后才让父进程打开写端或读端
,这是因为假如先让父进程打开写端或读端
,那么此时父进程就会进入阻塞
状态,导致无法创建子进程,自然也就无法再打开【读端或写端】; - 所以正确做法是先让子进程打开,即使子进程【阻塞】了,父进程也还能运行。不要让【阻塞】阻碍子进程的创建
子进程继承都存在的问题:写端重复继承,因此需要关闭不必要的写端 fd
关于问题一的理解可以看看下面这两张图:
正确用法: 先创建子进程,让子进程打开【读端或写端】
,再让父进程打开【写端或读端】
小结
以上就是本次关于 Linux进程间通信之命名管道的全部内容了,作为匿名管道的兄弟,命名管道具备匿名管道的大部分特性,使用方法也基本一致,不过二者在创建和打开方式上各有不同:匿名管道简单,但只能用于具有血缘关系进程间通信,命名管道虽麻烦些,但适用于所有进程间通信场景。
本篇关于命名管道的介绍就暂告段落啦,希望能对大家的学习产生帮助,欢迎各位佬前来支持斧正!!!
相关文章:

在虚拟宇宙中低语——进程间通信,Linux命名管道的前世今生
文章目录 🌌 序章🌠 一、命名管道的宿命与哲学1.1、创建及简单使用1.2、命名管道的工作原理1.3、命名管道与匿名管道的区别 2、命名管道的特点及特殊场景2.1、特点2.2、四种特殊场景 3、命名管道实操3.1、实现文件拷贝3.2、实现进程控制 小结 dz…...
Cursor 工具项目构建指南:Java 21 环境下的 Spring Boot Prompt Rules 约束
简简单单 Online zuozuo: 简简单单 Online zuozuo 简简单单 Online zuozuo 简简单单 Online zuozuo 简简单单 Online zuozuo :本心、输入输出、结果 简简单单 Online zuozuo : 文章目录 Cursor 工具项目构建指南:Java 21 环境下的 Spring Boot Prompt Rules 约束前言项目简…...
各个布局的区别以及示例
各个布局的区别以及示例 在前端开发中,常见的布局方式主要有以下几种,每种布局都有其适用场景和特点: 1. 普通文档流(Normal Flow) 特点:默认布局方式,元素按照HTML顺序依次排列。适用场景&am…...
什么是MVC?
导语: 在Java后端面试中,“MVC架构”是绕不开的基础话题。它不仅关乎项目的整体设计思路,更体现了候选人的架构理解能力与编码规范意识。本文将从面试官视角出发,结合高频问题、代码示例、答题技巧与加分项,带你全面吃…...

STM32的ADC简介
一、ADC简介 STM32的ADC是一种12位逐次逼近型模拟数字转换器。它具备18个通道,能够测量16个外部信号源以及2个内部信号源。各通道的A/D转换可以执行单次、连续、扫描或间断模式。转换结果可采用左对齐或右对齐的方式(12位)存储于16位数据寄存…...

Bash shell四则运算
文章目录 四则运算1. expr 命令2. $(( )) 表达式(推荐)3. $[ ] 表达式(已弃用)4. let 命令小数运算i 和 i 区别 四则运算 算术运算: - * / %(取模,求余数) Bash sh…...

(javaSE)Java数组进阶:数组初始化 数组访问 数组中的jvm 空指针异常
数组的基础 什么是数组呢? 数组指的是一种容器,可以用来存储同种数据类型的多个值 数组的初始化 初始化:就是在内存中,为数组容器开辟空间,并将数据存入容器中的过程。 数组初始化的两种方式:静态初始化,动态初始化 数组的静态初始化 初始化…...

力扣刷题Day 70:在排序数组中查找元素的第一个和最后一个位置(34)
1.题目描述 2.思路 方法1(自己写的):一次二分查找找到等于target的一个元素索引axis,然后向左右延伸找边界。 方法2(灵茶山艾府佬的闭区间二分查找写法):定义一个lower_bound()函数找到第一个…...
vue 多端适配之pxtorem
在 Vue 3 Vite 项目中使用 postcss-pxtorem 自动将 px 单位转换为 rem 单位,可以按照以下步骤配置: 一、基础版本 1. 安装依赖 首先安装必要的插件: npm install postcss postcss-pxtorem autoprefixer -D # 或 yarn add postcss postcs…...

图片压缩工具 | 图片属性详解及读取解析元数据
ℹ️ 图片信息及属性 基本属性 格式类型:JPEG、PNG、GIF、WEBP、BMP、TIFF等文件大小:以KB、MB等为单位的存储空间占用创建/修改日期:文件的元数据时间戳 视觉属性 尺寸/分辨率 宽度(像素)高度(像素&…...
React---day8
9.6 不可变数据的力量 我们知道是不能够修改this.state里面的数据的 举个例子 export class App extends React.PureComponent{constructor(){super();this.state {userList:[{name : "tom" , age : 18},{name : "lily" , age : 20},{name : "tik…...

C# Onnx 动漫人物人脸检测
目录 效果 模型信息 项目 代码 下载 参考 效果 模型信息 Model Properties ------------------------- stride:32 names:{0: face} --------------------------------------------------------------- Inputs ------------------------- name&am…...

C++内存列传之RAII宇宙:智能指针
文章目录 1.为什么需要智能指针?2.智能指针原理2.1 RAll2.2 像指针一样使用 3.C11的智能指针3.1 auto_ptr3.2 unique_ptr3.3 shared_ptr3.4 weak_ptr 4.删除器希望读者们多多三连支持小编会继续更新你们的鼓励就是我前进的动力! 智能指针是 C 中用于自动…...

PVE 虚拟机安装 Ubuntu Server V24 系统 —— 一步一步安装配置基于 Ubuntu Server 的 NodeJS 服务器详细实录1
前言 最近在基于 NodeJS V22 写一个全栈的项目,写好了,当然需要配置服务器部署啦。这个过程对于熟手来说,还是不复杂的,但是对于很多新手来说,可能稍微有点困难。所以,我把整个过程全部记录一下。 熟悉我…...
GitHub 趋势日报 (2025年06月03日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 2404 onlook 860 system-design-primer 380 nautilus_trader 372 agent-zero 357 …...
出现dev/nvmeOnip2 contains a file system with errors, check forced 解决方法
目录 前言1. 问题所示2. 原理分析3. 解决方法4. 彩蛋前言 爬虫神器,无代码爬取,就来:bright.cn Java基本知识: java框架 零基础从入门到精通的学习路线 附开源项目面经等(超全)【Java项目】实战CRUD的功能整理(持续更新)1. 问题所示 出现如下问题: dev/nvmeOnip2 co…...
Vue3.5 企业级管理系统实战(二十二):动态菜单
在前几篇内容中已完成菜单、角色及菜单权限等相关开发,若要在左侧菜单根据用户角色动态展示菜单,需对 Sidebar 中的相关数据进行修改。鉴于其他相关方法及类型已在前文实现,本文不再重复阐述。 1 修改 Sidebar 组件 在 src/layout/componen…...
磨皮功能 C++/C的OpenCV 实现
磨皮功能 C/C的OpenCV 实现 前提条件 OpenCV 安装: 你需要正确安装 OpenCV 库。C 编译器: 如 G。 C 代码 #include <opencv2/opencv.hpp> #include <iostream> #include <string>// 使用标准命名空间 using namespace std; using …...
蓝牙防丢器应用方案
蓝牙防丢器通常由两个主要部分构成:一个小型装置,亦称为标签,以及一个与之配对的手机应用程序。该标签内置一个微型蓝牙芯片,能够与配对的手机应用程序进行通信。一旦标签与手机之间的连接中断,手机应用程序便会接收到…...

TDengine 开发指南——高效写入
高效写入 本章内容将介绍如何发挥 TDengine 最大写入性能,通过原理解析到参数如何配置再到实际示例演示,完整描述如何达到高效写入。 为帮助用户轻松构建百万级吞吐量的数据写入管道,TDengine 连接器提供高效写入的特性。 启动高效写入特性…...

Linux kill 暂停命令
暂停进程 kill -19 在一台服务器上部署了360Pika服务,先用RedisClient连接一下,可以连接 现在暂停进程 暂停后发现再次连接无法连接 恢复进程 kill -18 恢复后可连接...
Unity与Excel表格交互热更方案
在Unity中实现与Excel表格的交互并支持热更是许多游戏开发中的常见需求。以下是几种实现方案: 1. 使用ScriptableObject存储表格数据 实现步骤: 将Excel表格导出为CSV格式 编写编辑器脚本将CSV数据导入到ScriptableObject 在运行时通过Resources或Ad…...
LVS、NGINX、HAPROXY的调度算法
目录 一、LVS(Linux Virtual Server)调度算法 (一)静态调度算法 (二)动态调度算法 二、NGINX调度算法 (一)内置调度算法 (二)第三方模块支持的调度算法…...
C++ 使用 ffmpeg 解码本地视频并获取每帧的YUV数据
一、简介 FFmpeg 是一个开源的多媒体处理框架,非常适用于处理音视频的录制、转换、流化和播放。 二、代码 示例代码读取一个本地视频文件,解码并将二进制文件保存下来。 注意: 代码中仅展示了 YUV420P 格式,其他 NV12/NV2…...
分布式微服务系统架构第143集:pom文件
加群联系作者vx:xiaoda0423 仓库地址:https://webvueblog.github.io/JavaPlusDoc/ https://1024bat.cn/ https://github.com/webVueBlog/fastapi_plus https://webvueblog.github.io/JavaPlusDoc/ ✅ 各字段说明及是否可改 字段名说明是否可修改修改建议…...

2.0 阅读方法论与知识总结
引言 本文将详细分析考研英语阅读做题步骤,并对方法论进行总结,最后通过真题练习巩固方法。 一、做题步骤 所有技巧都建立在精读真题的基础上!建议按以下节奏复习: 1️⃣ 做题 先看题干了解文章大致主旨(看看有没有…...

5. Qt中.pro文件(1)
本节主要讲.pro文件的作用和一些相关基础知识与操作。 本文部分ppt、视频截图原链接:[萌马工作室的个人空间-萌马工作室个人主页-哔哩哔哩视频] 1 PRO文件 1.1 pro文件作用 添加需要用到的QT模块,如通过QT module_name来添加需要用到的Qt模块。指定生…...
第八部分:第三节 - 事件处理:响应顾客的操作
用户与界面的互动是通过事件触发的,比如点击按钮、在输入框中输入文本、提交表单等。React 提供了一套跨浏览器的事件系统,让我们可以在组件中方便地处理这些事件。这就像点餐系统需要能够识别顾客的各种操作(按键、滑动屏幕)并作…...
共识机制全景图:PoW、PoS 与 DAG 的技术对比
目录 共识机制全景图:PoW、PoS 与 DAG 的技术对比 🧱 一、工作量证明(PoW) 原理概述 优点 缺点 示例代码(Python) 💰 二、权益证明(PoS) 原理概述 优点 缺点 …...
学习笔记085——Spring Data JPA笔记
1、什么是Spring Data JPA? Spring Data JPA 是 Spring 框架的一个子项目,它简化了基于 JPA (Java Persistence API) 的数据访问层的实现。它通过减少样板代码和提供默认实现,让开发者能够更快速地构建数据访问层。 1.1、主要特点 减少样板…...