进程间通信——管道
概念
进程间通信(Inter-Process Communication,简称 IPC)是指在不同进程之间进行数据交换和信息传递的机制。它的目的主要有4种:
- 数据传输:一个进程需要将它的数据发送给另一个进程
- 资源共享:多个进程之间共享同样的资源。
- 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
- 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。
在Linux中,主要的进程间通信有,管道、信号、System V进程间通信、POSIX进程间通信。这篇博客讲的是管道。而管道又分为匿名管道和有名管道。它们的区别是: - 匿名管道:只能在具有亲缘关系(父子进程或兄弟进程等)的进程间使用。它是一种半双工的通信方式,数据只能单向流动。匿名管道通常用于父子进程之间传递少量数据。
- 有名管道:可以在不相关的进程间使用。有名管道有一个特定的文件名,进程可以通过这个文件名来打开管道进行通信。有名管道也是半双工的。
管道通信原理
管道是Unix中最古老的进程间通信的形式。所谓管道,是指用于连接一个读进程和一个写进程,以实现进程之间通信的一种共享文件,又称为Pipe文件。向管道提供输入的是发送进程,或称为写进程,它负责向管道送入数据,数据的格式是字符流;而接收管道数据的接收进程称为读进程。由于发送进程和接收进程是利用管道来实现通信的,所以称为管道通信。
为了协调双方的通信,管道通信机制必须提供以下几个方面的协调能力。
- 互斥。当一个进程正在对管道进行读或写操作时,另一个进程必须等待。
- 同步。管道的大小是有限的。所以当管道满时,写进程必须等待,直到读进程把它召唤醒为止。同理,当管道没有数据时,读进程也必须等待,直到写进程将数据写入管道后,读进程才被唤醒。
- 对方是否存在。只有确认对方存在时,方能进行通信。
以上是一个进程,它打开了一些文件,于是在进程PCB
中的struct file_struct
成员中,就会指向当前进程已经打开的文件的结构体struct file
。
然后该进程创建了一个子进程:
该子进程会继承父进程的一些数据,同时也继承了file_struct
结构体,相当于2个进程同时打开了一份文件。
所以管道通信的原理就是:让进程之间发生PCB的继承,从而通过PCB打开同一份文件。
站在文件描述符的角度,理解管道
-
父进程创建管道
-
父进程通过fork创建子进程
-
父子进程关闭各自的读端或者写端(图片中父进程关闭读端,子进程关闭写端)
管道的通信是单向的:必须一端读,另一端写。
- 当父进程关闭写端时,那么子进程要关闭读端。这时父进程向管道中读取数据,子进程向管道中写入数据。
- 当父进程关闭读端时,那么子进程要关闭写端。这时父进程向管道中写入数据,子进程向管道中读取数据。
匿名管道
如果想要在系统中创建一个管道,那么就需要调用一个系统接口函数pipe
,它的头文件是<unistd.h>
。功能是创建一个匿名管道。它的函数原型是:
int pipe(int pipefd[2]);
其中,它的参数pipefd[2]
是一个文件描述符数组,也是一个输出型参数,pipedf[0]
表示读端,pipefd[1]
表示写端。
它的返回值有2个,当管道创建成功时返回0,当创建失败时,返回-1,并生成一个错误码。
我们先来看一下这个数组是代表什么意思。
int main()
{int pipefd[2] = {0};int n = pipe(pipefd);if (n < 0)return 1;cout<< "pipefd[0]:"<<pipefd[0]<<",pipefd[1]:"<<pipefd[1]<<endl;return 0;
}
可以发现3和4其实就是之前我们学习过的文件描述符的下标,只不过0,1,2已经被使用了。
主函数
int main()
{int pipefd[N] = {0};int n = pipe(pipefd);if (n < 0)return 1;// child用写的方式,father用读的方式pid_t id = fork();if (id < 0)return 2;if (id == 0){// 子进程close(pipefd[0]);// IPC codeWriter(pipefd[1]);}// 父进程close(pipefd[1]);Reader(pipefd[0]);waitpid(id, NULL, 0);return 0;
}
先通过pipe创建管道。如果n小于0说明管道创建失败,然后子进程关闭读端,打开写端进行写入;父进程关闭写端,打开读端读取数据。
Writer函数
void Writer(int wfd)
{string s = "hello,I am child";pid_t self = getpid();int number = 0;char buffer[NUM];while (true){// 构建发送字符串buffer[0] = 0; // 字符串清空,为了提醒阅读代码的人,我把这个数组当做字符串了snprintf(buffer, sizeof(buffer), "%s-%d-%d", s.c_str(), self, number++);// 发送/写入给父进程write(wfd, buffer, strlen(buffer));sleep(1);}
}
参数wfd是写端,其中NUM是1024。snprintf
函数是 C 语言标准库中的一个函数,用于格式化输出字符串,并将结果写入到指定的缓冲区5。其原型为:
int snprintf(char *str, size_t size, const char *format,...);
最后通过write
函数将buffer
数组中的内容写给wfd
。
Reader函数
void Reader(int rfd)
{char buffer[NUM];while (true){buffer[0] = 0;ssize_t n = read(rfd, buffer, sizeof(buffer));if (n > 0){buffer[n] = 0;cout << "father get a message[" << getpid() << "]#" << buffer << endl;}}
}
reader
函数会从管道中读取内容到buffer
中,随后输出pid
和buffer
中的内容。
运行程序,看看结果:
父进程成功读取到管道中的内容。
管道读写规则
当管道没有数据时,读进程也必须等待,直到写进程将数据写入管道后,读进程才被唤醒。
这是刚开始我们学习过的。我们发现Reader函数中没有sleep(1)
,只有Writer中有,输出结果的时候,每隔1秒输出一次。所以能够证明上述规则:管道中没有数据的时候,就进行等待,直到写端写入数据。写端每隔1秒才写入一条数据,自然读端每隔1秒才能读到数据。
写端一直写,读端不读取数据,直到管道写满时,写端将被阻塞,进入等待状态,暂停执行。这意味着写端进程不能继续进行其他与写入管道相关的操作,直到管道中有足够的空间可供写入新的数据。
现在我们修改Writer函数
void Writer(int wfd)
{string s = "hello,I am child";pid_t self = getpid();int number = 0;char buffer[NUM];while (true){char c = 'c';write(wfd,&c,1);number++;cout<<number<<endl;}
}
写端一直向管道中写入字符c,利用number计数,并且不调用Reader函数。
可以看到最后输出的number是65536,就不再写往管道中写入数据了,说明管道已经被写满了。我是在Centos 7.8版本
测试的,由此说明管道大小约为64kb。
当写端关闭时,此时读端依然可以读取到管道中的数据,不过read的返回值是0。说明读取到内容的结尾处。
继续修改Writer函数。
void Writer(int wfd)
{string s = "hello,I am child";pid_t self = getpid();int number = 0;char buffer[NUM];while (true){write(wfd, "x", 1);number++;if (number == 1024) //写入1024个字符后,终止循环,关闭写端break;}close(wfd);
}
Reader函数
void Reader(int rfd)
{char buffer[NUM];sleep(5);while (true){buffer[0] = 0;ssize_t n = read(rfd, buffer, sizeof(buffer));if (n > 0){buffer[n] = 0;cout << "father get a message[" << getpid() << "]#" << buffer << endl;}else if (n == 0) //等于0说明读取到文件末尾了{printf("father read file done!\n");break;}else{cout << "n:" << n << endl;}sleep(1);}
}
先让Reader休眠5秒,等到Writer写完后,通过read读取到管道中的内容。
当写端关闭后,读端把所有的数据都读取到了,随后输出father read file done!
说明返回值是0,由于数据已经被读完了,所以下次读取的时候没有数据可以读取,于是read的返回值是-1。
管道的读端被关闭后,写端进程直接终止。
对于写端进程来说,它通常会期望能够将数据写入管道,并由读端进程读取。如果读端关闭,写端进程继续写入数据将变得没有意义,因为没有接收者。
当写端进程尝试向一个已关闭读端的管道写入数据时,操作系统会检测到这种情况,并向写端进程发送一个特定的信号。这个信号通常是13号信号SIGPIPE,表示管道破裂。默认情况下,SIGPIPE 信号会导致写端进程终止。这是为了防止写端进程在无法成功写入数据的情况下无限期地阻塞或继续执行,从而浪费系统资源。
管道特点
- 只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道。
- 一般而言,进程退出,管道释放,所以管道的生命周期随进程。
- 管道提供流式服务
- 一般而言,内核会对管道操作进行同步与互斥
- 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道
有名管道
学习完匿名管道之后,接着学习一下有名管道。有名管道,顾名思义就是有名字的管道。之前我们学习的管道它的一个限制就是只能在具有亲缘关系之间才能够进行通信,通常使用匿名管道来实现。而如果我们想在==不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为有名管道。==有名管道是一种特殊类型的文件。
2个管道之间的区别
一、命名方式
- 匿名管道:没有特定的名称标识,通常在具有亲缘关系的进程(如父子进程)间使用。
- 有名管道:有一个特定的文件名,可以在不相关的进程间使用。有名管道的文件名存在于文件系统中,进程可以通过这个文件名来打开管道进行通信。
二、创建方式
- 匿名管道:通常在程序中通过调用特定的系统函数(如
pipe
函数)来创建。创建匿名管道后,会返回两个文件描述符,一个用于读,一个用于写。 - 有名管道:可以通过命令行工具(如
mkfifo
命令)或者在程序中使用特定的系统函数(如mkfifo
函数)来创建。创建有名管道后,它就像一个普通的文件,可以使用文件操作的方式(如open
、read
、write
等函数)来进行操作。
三、生命周期
- 匿名管道:通常随着创建它的进程的结束而被销毁。当父进程创建了匿名管道并创建了子进程后,如果父进程或子进程结束,匿名管道也会被关闭。
- 有名管道:有名管道的生命周期独立于创建它的进程。即使创建有名管道的进程结束了,只要还有其他进程在使用这个管道,它就会一直存在。只有当所有使用有名管道的进程都关闭了它,或者通过特定的命令(如
rm
命令)删除了有名管道的文件名,有名管道才会被销毁。
四、使用范围
- 匿名管道:只能在具有亲缘关系的进程间使用,因为它的创建和使用通常是在一个进程创建子进程的过程中进行的。例如,父进程创建匿名管道后,将其文件描述符传递给子进程,从而实现父子进程间的通信。
- 有名管道:可以在不相关的进程间使用,只要这些进程知道有名管道的文件名,就可以通过打开这个文件来进行通信。这使得有名管道在不同的应用场景中更加灵活,可以用于多个不相关的进程之间的协作。
创建方式
我们可以通过mkfifo
指令来创建一个有名管道;当然也可以通过函数接口进行创建。我们先通过指令来进行演示。语法形式是:mkfifo filename
可以看到我们通过mkfifo
创建了一个有名管道,它的文件表示是p
,并且文件大小是0,因为与磁盘上的普通文件不同,管道不用于存储数据以供将来读取。管道中的数据是临时的,仅当至少有一个进程正在写入且至少有一个进程正在读取时才存在。
当我们向管道中写入信息时,命令行就会进入阻塞状态,只有当别人读取管道中的内容时,阻塞状态才会被终止。
下面,我们使用函数接口的形式来演示。它的头文件是<sys/types.h>和<sys/stat.h>
,函数原型是:int mkfifo(const char *pathname, mode_t mode);
pathname
:表示的是有名管道创建的路径。mode
:文件的初始权限。
管道创建成功时返回0,创建失败时返回-1,并设置一个错误码。
我们执行下面代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>int main()
{umask(2);int n = mkfifo("./test", 0666);if(n == 0)printf("create sucess\n");elseprintf("create fail\n");return 0;
}
后续只需要通过在不同进程中通过open
接口打开这个文件,就可以进行进程间通信了。
如果想要销毁这个管道文件,只需要在程序中执行接口unlink("./test")
即可。它的函数原型是:
int unlink(const char *pathname);
,当被成功销毁时返回0,销毁失败时返回-1,并产生一个错误码。当然也可以直接使用指令unlink filename
来销毁管道文件。
在刚才代码中加入这个接口,并运行代码。
int x = unlink("./test");
if(x == 0){printf("delete sucess\n");
}
else{printf("delete fail\n");
}
有名管道打开的规则
- 如果当前打开操作是为读而打开FIFO时
- O_NONBLOCK disable:阻塞直到有相应进程为写而打开该FIFO
- O_NONBLOCK enable:立刻返回成功
- 如果当前打开操作是为写而打开FIFO时
- O_NONBLOCK disable:阻塞直到有相应进程为读而打开该FIFO
- O_NONBLOCK enable:立刻返回失败,错误码为ENXIO
有名管道的通信
接着我们来写一份代码来实现有名管道之间的通信。进程A向管道中写入信息,而进程B则向管道中读取信息。
进程A(test1.cpp)
#include <iostream>
#include <cstring>
#include <cerrno>
#include <cassert>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
using namespace std;int main()
{// 1.创建管道文件mkfifoumask(0); // 这个设置并不影响系统的默认配置,只会影响当前进程int n = mkfifo("./test", 0666);if (n == -1) // 失败返回-1,打印错误码并且退出1{cout << errno << " : " << strerror(errno) << endl;return 1;}// 2.让进程A开启管道文件int wfd = open("test", O_WRONLY);if (wfd == -1) // 失败返回-1,打印错误码并且退出2{cout << errno << " : " << strerror(errno) << endl;return 2;}// 3.向管道中写入"i am process A"while (true){char buffer[1024] = "i am A";ssize_t m = write(wfd, buffer, strlen(buffer));assert(m > 0);(void)m;sleep(1);}close(wfd);unlink("test"); // 销毁管道文件的链接,也就是删除test文件return 0;
}
进程B(test2.cpp)
#include <iostream>
#include <cstring>
#include <cerrno>
#include <cassert>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
using namespace std;int main()
{// 1.不需创建管道文件,只需要打开对应的文件即可int rfd = open("test", O_RDONLY);if (rfd == -1) // 失败返回-1,打印错误码并且退出1{cout << errno << " : " << strerror(errno) << endl;return 1;}// 2.可以进行常规通信了while (true){char buffer[1024];int n = read(rfd, buffer, sizeof(buffer));assert(n >= 0);cout << "我是B进程,读取到的数据是:" << buffer << endl;sleep(1);}close(rfd);return 0;
}
运行结果
如果出现以下错误,需要手动删除管道文件,再执行。
相关文章:
进程间通信——管道
概念 进程间通信(Inter-Process Communication,简称 IPC)是指在不同进程之间进行数据交换和信息传递的机制。它的目的主要有4种: 数据传输:一个进程需要将它的数据发送给另一个进程资源共享:多个进程之间…...
五一作业-day02
文章目录 1. 每日基操2. 模拟故障2.1 **remove regular empty file 是否删除普通文件(空的)?**2.2 **is a directory xxx是一个目录**2.3 **xxx not a directory 不是一个目录**2.4 Cant open file for writing2.5 **No write since last change** 3. 习题4. **进阶习题** 1. …...
Springclound常用五大组件及其使用原理
注册中心Eureka Eureka-Server:就是服务注册中心(可以是一个集群),对外暴露自己的地址。 提供者:启动后向Eureka注册自己信息(地址,服务名称等),并且定期进行服务续约 …...
Qt 显示QRegExp 和 QtXml 不存在问题
QRegExp 和 QtXml 问题 在Qt6 中 已被弃用; 1)QRegExp 已被弃用,改用 QRegularExpression Qt5 → Qt6 重大变更:QRegExp 被移到了 Qt5Compat 模块,默认不在 Qt6 核心模块中。 错误类型解决方法QRegExp 找不到改用 Q…...
开元类双端互动组件部署实战全流程教程(第4部分:后台配置系统与参数动态控制)
作者:曾经因为后台配置写错,导致全服进不去房的工程师 组件附带的后台管理系统为 PHP 编写,界面简洁但功能齐全。具备完整的模块划分与权限体系,支持动态参数下发、日志审计、行为数据统计等。 七、前端后台交互流程图与代码示例 …...
MySQL基础关键_008_DDL 和 DML(一)
目 录 一、DDL 1.创建表 (1)语法格式 (2)实例 2.查看建表语句 (1)语法格式 (2)实例 3.修改表名 (1)语法格式 (2)实例 4.新…...
基于SpringBoot + Vue 的火车票订票系统
包含: [1]源码✔ 数据库文件✔ [2]万字文档✔ [3]视频与图文配置教程✔ 功能描述: 本系统包含管理员、用户两个角色。 管理员:用户管理、新闻公告管理、车辆管理、车站及路线管理、留言建议管理、车次信息管理 用户:购票操作、查…...
飞致云开源社区月度动态报告(2025年4月)
自2023年6月起,中国领先的开源软件公司飞致云以月度为单位发布《飞致云开源社区月度动态报告》,旨在向广大社区用户同步飞致云旗下系列开源软件的发展情况,以及当月主要的产品新版本发布、社区运营成果等相关信息。 飞致云开源运营数据概览&…...
解决跨域的4种方法
00_跨域的概念 浏览器只允许请问具有相同的协议,域名,端口,进行请求,有一个不同,就会拒绝。 01.前后端协商jsonp //jsonp//jsonp 是 json with padding 的缩写,是一种通过 <script> 标签的 src 属性…...
C# 方法(局部函数和参数)
本章内容: 方法的结构 方法体内部的代码执行 局部变量 局部常量 控制流 方法调用 返回值 返回语句和void方法 局部函数 参数 值参数 引用参数 引用类型作为值参数和引用参数 输出参数 参数数组 参数类型总结 方法重载 命名参数 可选参数 栈帧 递归 局部函数 正如刚刚所解释的&…...
kotlin 02flow-sharedFlow 完整教程
一 sharedFlow是什么 SharedFlow 是 Kotlin 协程中 Flow 的一种 热流(Hot Flow),用于在多个订阅者之间 共享事件或数据流。它适合处理 一次性事件(如导航、弹窗、Toast、刷新通知等),而不是持续状态。 ✅ …...
数据库原理——E-R图的极速省流理解 例题解析
前言 数据库一节没听,一个小时看书给我大致看懂了 E-R概念模型极速省流版 E-R图的重点: 关系图,三要素——实体、属性、联系 图形标识——矩形、椭圆形、菱形 1.实体和属性也可以放一个框矩形框 2.菱形两层边:弱实体集的联…...
5.4 - 5.5Web基础+c语言拓展功能函数
StringBoot HTTP协议: 规定了浏览器与服务器之间数据传递的规则。 请求协议: 请求数据格式: 请求头和请求体之间有一个空行隔开 响应协议: 响应数据格式: 响应头和响应体之间存在空行隔开。 响应数据设置࿱…...
Java抽象类与接口详解
一、抽象类(Abstract Class) 1. 定义与基本使用 // 抽象类定义 public abstract class Animal {// 抽象方法(无实现)public abstract void makeSound();// 具体方法(有实现)public void sleep() {System.out.println("动物在睡觉");} }// 继承抽象类 class Dog ext…...
网络延时 第四次CCF-CSP计算机软件能力认证
就是求树的直径: 思路:函数代表当前根节点的最长距离 然后遍历保存当前树的所有孩子的最长距离 和次长距离 如果是叶子节点就返回0 在每次获得每个节点的次长距离和最长距离就更新全局直径 最后获得最长距离 Ac代码: #include <bits/stdc.h> using namespa…...
【C++进阶十】多态深度剖析
【C进阶十】多态深度剖析 1.多态的概念及条件2.虚函数的重写3.重写、重定义、重载区别4.C11新增的override 和final5.抽象类6.虚表指针和虚表6.1什么是虚表指针6.2指向谁调用谁,传父类调用父类,传子类调用子类 7.多态的原理8.单继承的虚表状态9.多继承的…...
网络传输中字节序
在小端字节序主机发送数据 0x1234 的情况下,(单字节没有字节序)我们可以分步骤来分析接收端如何解析这个数据: 1. 小端字节序主机的存储方式 在小端字节序中,低地址存储低字节,高地址存储高字节。 数据 0x1234 的字节表示为: 低字节:0x34 高字节:0x12 因此,在小端字…...
前端- ElementPlus入门
1.介绍 Element:是饿了么公司前端开发团队提供的一套基于 Vue3 的网站组件库,用于快速构建网页。 Element 提供了很多组件供我们使用。例如 超链接、按钮、图片、表格等等。 官方网站:一个 Vue 3 UI 框架 | Element Plus 2.步骤 1.安装E…...
AI Agent 要用到的技术
AI 发展是大趋势,以下是目前要用到的一些技术项 不论你从事哪个方向,这个技术栈都有必要学习 LangChainTransformersMicrosoft Semantic KernelLangflowLangGrphLangSmith 学习网站 以下是 LangChain、Transformers、Microsoft Semantic Kernel 的学习…...
# 从零构建一个简单的卷积神经网络:手写数字识别
从零构建一个简单的卷积神经网络:手写数字识别 在深度学习的世界里,卷积神经网络(CNN)是处理图像数据的强大工具。今天,我们将通过一个简单的例子,从零开始构建一个CNN模型,用于手写数字识别。…...
【RK3588嵌入式图形编程】-Cairo-Cairo图形库支持后端
Cairo图形库支持后端 文章目录 Cairo图形库支持后端1、PNG图像后端2、PDF文件后端3、SVG文件后端4、GTK窗口支持Cairo库支持多种后端。在本文中,我们使用Cairo创建PNG图像、PDF文件、SVG文件,并在GTK窗口上绘制。 1、PNG图像后端 在第一个示例中,我们创建一个 PNG 图像。 …...
LCD,LED
本文来源 : 腾讯元宝 LCD(Liquid Crystal Display)液晶显示器 LCD本身并不能发光,而是控制光的传输。 LCD内充满了棒状的液态分子(液晶),这些分子可以形成扭转的螺旋线,弯曲来自显示器背后光源产生的光线或…...
HTML 元素
什么是 HTML 元素? HTML 元素(Element)是构成 HTML 文档的基本单位,它由开始标签、内容和结束标签组成,用于定义网页的结构和内容。元素是 HTML 标记语言的核心概念,每个元素都有特定的语义和用途。 元素…...
(undone) MIT6.S081 2023 学习笔记 (Day11: LAB10 mmap)
url: https://pdos.csail.mit.edu/6.1810/2023/labs/mmap.html 任务0:讲义如下 (完成) mmap和munmap系统调用允许UNIX程序对其地址空间进行精细控制。它们可用于进程间共享内存、将文件映射到进程地址空间,并作为用户级页面错误处理方案的一部分&#x…...
7400MB/s5050TBW完美结合,全新希捷酷玩530R SSD体验评测
7400MB/s&5050TBW完美结合,全新希捷酷玩530R SSD体验评测 哈喽小伙伴们好,我是Stark-C~ 说到希捷酷玩530 SSD,很多硬核进阶玩家应该都知道,或者说正在使用(比如说我~)。 作为希捷大厂旗下高性能SSD的…...
【数据结构与算法】同余计算 哈希表与前缀和问题特征和模板化思路
加减乘负的类同余计算 加乘模情况 两数和模 : (a b) mod m (a mod m b mod m) mod m乘积模 : (a * b) mod m ((a mod m) * (b mod m)) mod m 加模证明如下 : a q1 * m r1 , b q2 * m r2则 a mod m r1 , b mod m r2(a b) mod m ((q1 q2) * m (r1 r2)) mod m …...
TS 交叉类型
很类似于接口继承啊 哈哈哈 使用type关键字声明交叉类型 // 声明交叉类型 type PersonDetail person & Contactlet theshy:PersonDetail { name:the shy,phone:马头 }交叉类型和接口实现对对象的类型注解的区别与联系 如果在接口继承时发生同名属性冲突会报不兼容的错…...
FreeRTOS学习系列·二值信号量
目录 1. 信号量的基本概念 2. 二值信号量 3. 应用场景 4. 运作机制 5. 信号量控制块 6. 常用信号量函数接口API 6.1 创建二值信号量 xSemaphoreCreateBinary() 6.2 信号量删除函数 vSemaphoreDelete() 6.3 信号量释放函数 6.3.1 xSemaphoreGive() 6.3.2 …...
二叉搜索树 AVL树 红黑树 的性质
二叉搜索树 如何判断一棵树是否是二叉搜索树? 1.方法一 左子树的所有节点值 < 当前节点值,右子树的所有节点值 > 当前节点值,左右子树也必须满足 。 2.方法二 中序遍历,得到的序列是有序的 红黑树 红黑树的性质 1.根…...
分析 Docker 磁盘占用
以下是分析 Docker 磁盘占用的详细步骤和工具指南,帮助开发者快速定位和清理冗余数据: 1. 查看 Docker 磁盘使用概览 docker system df 输出说明: TYPE TOTAL ACTIVE SIZE RECLAIMABLE Images 15 …...
LLM提示词设计及多轮对话优化策略在心理健康咨询场景中的应用研究
多轮对话策略:设计LLM提示词并优化多轮对话输出 LLM提示词设计及多轮对话优化策略在心理健康咨询场景中的应用研究 摘要 本文针对大语言模型(LLM)在心理健康问题咨询对话场景中的应用,系统研究提示词设计方法及多轮对话优化策略。通过分析提示词核心原理,构建包含任务指…...
Kubernetes(k8s)学习笔记(七)--KubeSphere 最小化安装
前情提要 可视化操作面板对于开发、运维绝对是提升工作效率的一大利器,因此很有必要搭建一套可视化操作来管理Kubernetes。 可视化面板有多种: 1.Kubernetes官方提供的默认面板:dashboard,用处不大,放弃;…...
面向对象与过程介绍
一、面向对象具体介绍 (1)基本概念 1.对象 在面向对象编程中,对象是对现实世界中事物的抽象,它具有状态(属性)和行为(方法)。在 TypeScript 中,我们可以通过类、接口等…...
UniGetUI 使用指南:轻松管理 Windows 软件(包括CUDA)
UniGetUI(前身为 WingetUI)是一款专门为 Windows 10(x64)和 Windows 11 系统打造的图形化包管理器界面工具。它集成了 Winget、Scoop、Chocolatey、Npm、Pip、Cargo、vcpkg、.NET Tool 和 PowerShell 等多种常用包管理器的功能&am…...
使用 NGINX 实现 HTTP Basic 认证ngx_http_auth_basic_module 模块
一、前言 在 Web 应用中,对部分资源进行访问控制是十分常见的需求。除了基于 IP 限制、JWT 验证、子请求校验等方式外,最经典也最简单的一种方式便是 HTTP Basic Authentication。NGINX 提供的 ngx_http_auth_basic_module 模块支持基于用户名和密码的基…...
014枚举之指针尺取——算法备赛
枚举是数据结构与算法中基本的操作,常用于解决序列的区间问题。算法界将"双指针"视为其重要分支,类似地当然还有"三指针",“四指针”,最常见的还是“双指针”,我认为它们应统称为“指针尺取”。 双…...
Python小酷库系列:bidict,可以双向查询的dict
bidict,可以双向查询的dict 基本使用创建双向字典正向查找(key → value)反向查找(value → key)添加新项删除项 进阶功能使用 ~ (.inverse)获取逆映射使用 namedbidict 创建具名双向字典批量更…...
Android Compose 中 CompositionLocal 的全面解析与最佳实践
CompositionLocal 在 Android Compose 中的详细总结 核心概念 CompositionLocal 是 Jetpack Compose 提供的一种隐式数据传递机制,允许数据在组件树中向下传递,而无需显式地通过每个 Composable 函数的参数传递。 两种创建方式 1. staticComposition…...
Qt开发:容器组控件的介绍和使用
文章目录 一、Group Box(分组框)1.1 QGroupBox 简介1.2 基本用法1.3 设置为可勾选(可启用/禁用子控件)1.4 信号与槽连接(监控勾选状态)1.5 布局示例(完整) 二、Scroll Areaÿ…...
JS逆向入门案例1——集思录登录
JS逆向入门案例1——集思录登录 前言声明网站流程分析总结 前言 由于这段时间本职工作比较繁忙,没有很多空余的时间去研究各大厂的加密风控了,想起来自己刚接触js逆向走过坎坷,所以决定出一期js入门案例分析,为刚接触js逆向的小伙…...
ARM子程序和栈
微处理器中的栈由栈指针指向存储器中的栈顶来实现,当数据项入栈时,栈 指针向上移动,当数据项出栈时,栈指针向下移动。 实现栈时需要做出两个决定:一是当数据项进栈时是向低位地址方向向上生 长(图a和图b&a…...
笔试专题(十五)
文章目录 排序子序列题解代码 消减整数题解代码 最长公共子序列(二)题解代码 排序子序列 题目链接 题解 1. 贪心 模拟 2. 1 2 3 2 2 应该是有两个排列子序列的,所以i n-1时ret 3. 把水平的位置和上升部分,水平位置和下降部分分为一个排列子序列 代…...
使用OpenCV 和 Dlib 进行卷积神经网络人脸检测
文章目录 引言1.准备工作2.代码解析2.1 导入必要的库2.2 加载CNN人脸检测模型2.3 加载并预处理图像2.4 进行人脸检测2.5 绘制检测结果2.6 显示结果 3.完整代码4.性能考虑5.总结 引言 人脸检测是计算机视觉中最基础也最重要的任务之一。今天我将分享如何使用dlib库中的CNN人脸检…...
某信服EDR3.5.30.ISO安装测试(一)
一、前言 1.某信服EDR3.5.30 以下简称“EDR3.5”,即统一端点安全管理系统aES(终端检测响应EDR), 官网最新版:aES6.0.1R2,可下载的最低版本:EDR3.7.11R3, 下载地址:统一端点安全管理系统aES-…...
Dify 快速构建和部署基于LLM的应用程序
本文先对Dify做一个初步的认识,然后以一个实际的简单金融问答案例,配置chatflow 工作流。 一、Dify简介 如果你是第一次接触Dify,可以先创建一个简单的聊天助手,初步感觉一下,Dify在构建聊天问答类应用的过程。 比如…...
精益数据分析(40/126):移动应用商业模式的关键指标与盈利策略
精益数据分析(40/126):移动应用商业模式的关键指标与盈利策略 在创业和数据分析的探索之路上,我们持续挖掘不同商业模式的内在规律,以寻求更好的发展机遇。今天,我们依旧秉持共同进步的理念,深…...
JavaScript 实现输入框的撤销功能
在 Web 开发中,为输入框添加撤销功能可以极大地提升用户体验,方便用户快速回滚到之前的输入状态。本文将通过一段简单的 HTML、CSS 和 JavaScript 代码,详细介绍如何实现输入框的撤销功能。 整体实现思路 利用 JavaScript 监听输入框的inpu…...
【C++】类和对象(一)
前言 类和对象第一部分知识包括定义访问限定符类域实例化this指针 本人其他文章:恋风诗 文章中的源码[gitte]:mozhengy 类和对象(一) 前言1. 类的定义引例1.1 类定义格式1.2 类的访问限定符1.3 类域 2. 实例化2.1 实例化概念2.2 …...
【Vue】Vue3源码解析与实现原理
个人主页:Guiat 归属专栏:Vue 文章目录 1. Vue 3 架构概览1.1 模块化设计1.2 整体流程 2. 响应式系统2.1 响应式原理2.2 ref 和 reactive2.3 依赖收集与触发更新 3. 渲染系统3.1 虚拟DOM设计3.2 渲染管线3.3 Patch算法与Diff优化 4. 组件系统4.1 组件创建…...
黑马点评day02(缓存)
2、商户查询缓存 2.1 什么是缓存? 前言:什么是缓存? 就像自行车,越野车的避震器 举个例子:越野车,山地自行车,都拥有"避震器",防止车体加速后因惯性,在酷似"U"字母的地形上飞跃,硬着陆导致的损害,像个弹簧一样; 同样,实际开发中,系统也需要"避震…...