【linux】进程间通信——system V
system V
- 一、system V介绍
- 二 、共享内存
- 2.1 共享内存的原理
- 2.2 共享内存接口
- 2.2.1 创建共享内存shmget
- 2.2.2 查看IPC资源
- 2.2.3 共享内存的控制shmctl
- 2.2.4 共享内存的关联shmat
- 2.2.5 共享内存的去关联shmdt
- 2.3 进程间通信
- 2.4 共享内存的特性
- 2.5 共享内存的大小
- 三、消息队列
- 3.1 消息队列的概念
- 3.2 消息队列接口
- 3.2.1 消息队列的获取msgget
- 3.2.2 消息队列的控制msgctl
- 3.2.3 消息队列发送数据msgsnd
- 3.2.4 消息队列获取msgrcv
- 四、信号量
- 4.1 信号量概念
- 4.2 信号量的用处
- 4.3 信号量的pv操作
- 4.4 信号量接口
- 4.4.1 信号量申请semget
- 4.4.2 信号量控制semctl
- 4.4.3 信号量操作semop
- 五、总结
一、system V介绍
进程间通信除了通过管道,都是基于文件的通信方式,还有一种方式是:SystemV标准的进程间通信方式。SystemV是一个在OS层面专门为进程通信设计的一个方案。这些都是由计算机科学家和程序员设计的,并且需要给用户使用。
如果要给用户用,是以什么方式给用户使用的呢?在操作系统层面上,SystemV是OS内核的一部分,是为OS中多进程提供的一种通信方案。但是OS不相信任何用户,给用户提供功能的时候,采用系统调用。所以System V进程间通信,一定会存在专门用来通信的接口:system call。
因为在早期由很多的方案,但是我们需要统一使用一个方案,所以现在诞生了在统一主机内的进程间通信方案:system V方案。
system V IPC提供的通信方式有三种: 共享内存、消息队列、信号量
二 、共享内存
2.1 共享内存的原理
前面说过两个进程要通信就需要看到同一块资源。
而我们知道进程之间具有独立性,物理内存当中代码和数据也互相独立。
那么现在我们可以在物理内存中创建一个内存块,让不同的进程都能看到这个内存块,具体的做法如下:
1️⃣ 通过某种调用,在内存中创建一份内存空间。
2️⃣ 通过某种调用,让进程”挂接“到这份内存空间上。(将创建好的内存映射进进程地址空间)
在后面可能不会共享内存了。所以在不用共享内存的时候
3️⃣ 去关联(去挂接)。
4️⃣ 释放共享内存。
对于共享内存的理解:
OS内可能存在多个进程同时使用不同的共享内存来进行进程间通信,既然有多份共享内存,那么操作系统就要管理它们,按照前面学习的经验,先描述后组织,描述就是对共享内存的一系列属性进行描述,而后用数据结构组织起来,这样对共享内存的管理变成了对数据结构的操作。
2.2 共享内存接口
2.2.1 创建共享内存shmget
shmget:用来创建共享内存
#include <sys/ipc.h>#include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);RETURN VALUEOn success, a valid shared memory identifier is returned. On errir, -1 is returned, and errno is set to indicate the error.
参数说明:
size:共享内存的大小。
shmflag:通常有两种选项:IPC_CREAT、IPC_EXCL。
IPC_CREAT: 共享内存如果不存在,则创建,不存在则获取。
IPC_EXCL: 无法单独使用
IPC_CREAT | IPC_EXCL: 如果不存在就创建,如果存在就出错返回(保证共享内存是新创建的)。
如果shmflag是0默认就是IPC_CREAT。
key:保证看到同一份共享内存,能进行唯一性标识(就像省份证号码一样,数字不重要,只用来标识唯一性)。通过ftok函数转化。
如果创建成功返回共享内存的标识符,如果失败则返回-1。
ftok:用来形成key
#include <sys/types.h>
#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);
RETURN VALUE
On success, the generated key_t value is returned.
On failure -1 is returned, with errno indicating the error as for the stat(2) system call.
ftok第一个参数是自定义路径名,第二个参数是自定义的项目ID。最后唯一的共享内存ID就是通过路径名+项目ID来标识。最后生成的返回值并不重要,只要它能生成一个值来唯一标识这块共享内存就可以。
只要参数不变,生成的返回值就不会变。 这样就可以让两个进程拥有同一个key,就可以用key找到同一份共享内存。
key_t GetKey()
{key_t n = ftok(PATHNAME, PROJ_ID);if(n == -1){std::cerr << errno << ":" << strerror(errno) << std::endl;assert(false);}return n;
}int Creatshm(key_t key)
{int shmid = shmget(key, SIZE, IPC_CREAT | IPC_EXCL | 0666);if(shmid == -1){std::cerr << errno << ":" << strerror(errno) << std::endl;assert(false);}return shmid;
}int Getshm(key_t key)
{int shmid = shmget(key, SIZE, IPC_CREAT | 0666);if(shmid == -1){std::cerr << errno << ":" << strerror(errno) << std::endl;assert(false);}return shmid;
}
key的理解:
上面也说过了OS会把内存块管理起来,共享内存=物理内存块+共享内存的相关属性。描述共享内存时就有一个字段struct shm中有key。 一个进程创建内存块后把key值写进相关属性中,而另一个进程拿着key值遍历相关属性查找。这样就完成了两个进程共享一块内存块。
我们发现key和shmid都是标识内存块的:key是在内核标识唯一性,而shmid是在用户层标识唯一性,这样即使操作系统有什么变化也不会影响用户使用,充分的解耦。他们的关系就类似于fd与inode。
2.2.2 查看IPC资源
共享内存的生命周期不是随着进程的,而是随着OS的,这也是所有system V进程间通信的特征。
ipcs -m 查看共享内存

ipcs -q 查看消息队列
ipcs -s 查看信号量

ipcs 三个一起查看

ipcrm -m shmid 删除共享内存

2.2.3 共享内存的控制shmctl
shmctl:控制共享内存
#include <sys/ipc.h>
#include <sys/shm.h>int shmctl(int shmid, int cmd, struct shmid_ds *buf);
它的作用是对共享内存的控制。
参数介绍:
shmid:控制共享内存的标识符。
cmd:控制的种类,主要用的是IPC_RMID(立即移除共享内存)。
buf:控制共享内存的数据结构,设置为空即可。
返回值:0表示返回成功,-1表示失败。
void Delshm(int shmid)
{if(shmctl(shmid, IPC_RMID, nullptr) == -1){std::cerr << errno << ":" << strerror(errno) << std::endl;assert(false);}
}
2.2.4 共享内存的关联shmat
#include <sys/types.h>
#include <sys/shm.h>void *shmat(int shmid, const void *shmaddr, int shmflg);
参数介绍:
shmaddr:可以指定虚拟内存,设置为nullptr即可。
shmflg:读取权限,默认为0。
返回值:返回共享内存的起始地址。
void* Attachshm(int shmid)
{void* mem = shmat(shmid, nullptr, 0);if((long long)mem == -1L)// 64位指针8字节{std::cerr << errno << ":" << strerror(errno) << std::endl;assert(false);}return mem;
}
2.2.5 共享内存的去关联shmdt
去关联是指把进程和共享内存之间的映射关系删掉(修改页表),并不是删除共享内存。
#include <sys/types.h>
#include <sys/shm.h>int shmdt(const void *shmaddr);
它的参数就是在shmat返回的参数。
成功返回0,失败返回-1。
void Detachshm(void* start)
{if(shmdt(start) == -1){std::cerr << errno << ":" << strerror(errno) << std::endl;assert(false);}
}
2.3 进程间通信
comm.hpp
#ifndef _COMM_HPP_
#define _COMM_HPP_#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cstring>
#include <cerrno>
#include <cassert>
#include <cstdlib>
#include <cstdio>
#include <unistd.h>#define PATHNAME ".."
#define PROJ_ID 12345
#define SIZE 4096key_t GetKey()
{key_t n = ftok(PATHNAME, PROJ_ID);if(n == -1){std::cerr << errno << ":" << strerror(errno) << std::endl;assert(false);}return n;
}int Creatshm(key_t key)
{int shmid = shmget(key, SIZE, IPC_CREAT | IPC_EXCL | 0666);if(shmid == -1){std::cerr << errno << ":" << strerror(errno) << std::endl;assert(false);}return shmid;
}int Getshm(key_t key)
{int shmid = shmget(key, SIZE, IPC_CREAT | 0666);if(shmid == -1){std::cerr << errno << ":" << strerror(errno) << std::endl;assert(false);}return shmid;
}void Delshm(int shmid)
{if(shmctl(shmid, IPC_RMID, nullptr) == -1){std::cerr << errno << ":" << strerror(errno) << std::endl;assert(false);}
}void* Attachshm(int shmid)
{void* mem = shmat(shmid, nullptr, 0);if((long long)mem == -1L)// 64位指针8字节{std::cerr << errno << ":" << strerror(errno) << std::endl;assert(false);}return mem;
}void Detachshm(void* start)
{if(shmdt(start) == -1){std::cerr << errno << ":" << strerror(errno) << std::endl;assert(false);}
}#endif
shm_a.cc
#include "comm.hpp"int main()
{key_t k = GetKey();// 创建共享内存int shmid = Creatshm(k);// 挂接char* start = (char*)Attachshm(shmid);//使用while(true){if(strlen(start) > 4){printf("%s\n", start);}if(strlen(start) == 4){break;}sleep(1);}// 去关联Detachshm(start);// 删除共享内存Delshm(shmid);return 0;
}
shm_b.cc
#include "comm.hpp"int main()
{key_t k = GetKey();// 获取共享内存int shmid = Getshm(k);// 挂接char* start = (char*)Attachshm(shmid);// 使用std::string str = "hello shm";int cnt = 0;while(cnt != 8){snprintf(start, SIZE, "%s[%d]", str.c_str(), cnt);cnt++;sleep(1);}std::string cmd = "stop";snprintf(start, SIZE, "%s", cmd.c_str());// 去关联Detachshm(start);return 0;
}
2.4 共享内存的特性
1️⃣ 共享内存是所有的进程间通信速度最快的。(优点)
2️⃣ 共享内存不提供任何同步或者互斥机制,不提供不代表不需要,所以需要程序员自行保证数据的安全!这也造成了共享内存在多进程中是不太安全的。(缺点)
3️⃣ 共享内存的生命周期是随OS的,而不是随进程的,这是所有System V进程间通信的共性。
为什么速度快呢?
管道和共享内存:考虑键盘输入,和显示器输出,对于同一份数据,共享内存有几次数据拷贝,管道有几次数据拷贝:
管道:
可以看到写到管道需要拷贝两次,而另一个进程读也需要两次,所以一共四次。如果考虑到输入输出外设,那么就是六次。
共享内存:
直接写入共享内存,直接从共享内存输出,所以是两次。考虑到输入输出的话就是四次。
2.5 共享内存的大小
这里讲的是shget的第二个参数,一般建议设置成4096(4KB)的整数倍,因为系统分享内存是以4KB为单位,假如我们申请的是4097,那么系统就会直接向上取整,也就是4096*2,但是我们只能使用其中的4097大小。
三、消息队列
3.1 消息队列的概念
消息队列提供了一个从一个进程向另外一个进程发送一块数据的方法,读端和写端公用一个队列,每个数据块就是队列的一个节点,每个数据块都会有个记录类型的数据,来判断该数据块该被哪个进程读取。
3.2 消息队列接口
3.2.1 消息队列的获取msgget
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>int msgget(key_t key, int msgflg);
这里的msgflg跟共享内存的两个参数一摸一样(IPC_CREAT、IPC_EXCL)。
返回值:msgget函数返回的一个有效的消息队列标识符
3.2.2 消息队列的控制msgctl
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>int msgctl(int msqid, int cmd, struct msqid_ds *buf);
参数说明:
msqid:消息队列的标识符。
其他的参数跟shmctl一样。
3.2.3 消息队列发送数据msgsnd
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
参数说明:
msqid:消息队列的用户级标识符。
msgp:表示待发送的数据块(输出型参数)。

msgsz:表示所发送数据块的大小
msgflg:表示发送数据块的方式,一般默认为0即可
成功返回0,失败返回-1
3.2.4 消息队列获取msgrcv
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg)
参数跟上面msgsnd一致。
四、信号量
4.1 信号量概念
信号量本质上就是个计数器,它统计的是公共资源资源还剩多少。
公共资源:可以被多个进程同时访问的资源,而如果访问没有被保护的公共资源,就会导致数据不一致问题(一个进程还在写的时候另一个进程就开始读)。所以公共资源需要保护,被保护起来的资源称为临界资源。而访问这些临界资源的那部分代码称为临界区。其他的代码就称为非临界区。
保护公共资源:同步和互斥
互斥:当有多个进程想要访问同一份资源的时候,我们只允许一个进程访问,当这个进程访问完了,下一个进程才能访问。
原子性:要么不做,要么做完,只有这两种状态的情况。
既然我们想让多个进程看到同一个计数器,那么信号量也是个公共资源。
4.2 信号量的用处
举个例子,假设我们要去看电影,而里面的座位只有我们买了票才能拥有,这里的票就是信号量,我们买了一张后,信号量就--。
所以当我们想要某种资源的时候,我们可以进行预定(买票成功)。
共享资源可以作为一个整体使用或者划分成多个子资源部分。大部分都是整体使用,比如果管道。
如果我们申请成功,相当于我们预定了共享内存中的一小部分资源。如果不成功,就不能访问共享资源,以达到保护其他进程的目的。
在访问公共资源前要先申请信号量,而信号量本身就是个公共资源,那么信号量也带保护自己的安全。那么如何保证呢?
4.3 信号量的pv操作
信号量操作本质上就是计数器的++或者--,是原子性的。当我们预定资源的时候(--)称为p操作,释放资源(++)称为v操作。
如果信号量的初始值是1就代表了访问公共资源作为一个整体来使用,一个进程申请了别的进程就不能再申请了,我们把只有两种状态的信号量称为二元信号量。
4.4 信号量接口
4.4.1 信号量申请semget
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>int semget(key_t key, int nsems, int semflg);
参数介绍:
nsems:表示申请信号量的个数
第一个和第三个参数就跟前面的shmget相同。
返回值:信号量集创建成功时,semget函数返回的一个有效的信号量集标识符
4.4.2 信号量控制semctl
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
参数介绍:
semid:信号量标识符
semnum:信号量的下标,默认0
4.4.3 信号量操作semop
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, unsigned nsops);
参数介绍:
sembuf:一个结构体
sem_num:信号量下标。
sem_op:1表示++p操作,-1表示--v操作。
sem_flg:选项设为0即可。
nsops:有多少个sembuf结构体。
五、总结
我们可以发现,共享内存、消息队列、信号量接口相似度非常高,尤其是获取与删除,这些都是system V标准的进程间通信。
他们的第一个成员都是ipc_perm
struct ipc_perm {key_t __key; /* Key supplied to shmget(2) */uid_t uid; /* Effective UID of owner */gid_t gid; /* Effective GID of owner */uid_t cuid; /* Effective UID of creator */gid_t cgid; /* Effective GID of creator */unsigned short mode; /* Permissions + SHM_DEST andSHM_LOCKED flags */unsigned short __seq; /* Sequence number */};
我们可能会申请共享内存、消息队列、信号量的任意一种,那么操作系统如何组织它们呢?
因为ipc_perm是一样的,所以可以维护一个struct ipc_perm*的指针数组,存共享内存、消息队列、信号量它们的第一个元素的地址,也就是&ipc_perm。
而我们知道结构体的第一个成员的地址和结构体对象的地址在数字上是相等的。
所以当我们想使用的时候直接强转成想用的结构体(共享内存、消息队列、信号量)就可以。
相关文章:
【linux】进程间通信——system V
system V一、system V介绍二 、共享内存2.1 共享内存的原理2.2 共享内存接口2.2.1 创建共享内存shmget2.2.2 查看IPC资源2.2.3 共享内存的控制shmctl2.2.4 共享内存的关联shmat2.2.5 共享内存的去关联shmdt2.3 进程间通信2.4 共享内存的特性2.5 共享内存的大小三、消息队列3.1 …...
计算机网络的基本组成
计算机网络是由多个计算机、服务器、网络设备(如路由器、交换机、集线器等)通过各种通信线路(如有线、无线、光纤等)和协议(如TCP/IP、HTTP、FTP等)互相连接组成的复杂系统,它们能够在物理层、数…...
【数据结构趣味多】Map和Set
1.概念及场景 Map和set是一种专门用来进行搜索的容器或者数据结构,其搜索的效率与其具体的实例化子类有关。 在此之前,我还接触过直接查询O(N)和二分查询O(logN),这两个查询有很多不足之出,直接查询的速率太低,而二分查…...
Redis 之企业级解决方案
文章目录一、缓存预热二、缓存雪崩三、缓存击穿四、缓存穿透五、性能指标监控5.1 监控指标5.2 监控方式🍌benchmark🍌monitor🍌slowlog提示:以下是本篇文章正文内容,Redis系列学习将会持续更新 一、缓存预热 1.1 现象…...
雷达实战之射频前端配置说明
在无线通信领域,射频系统主要分为射频前端,以及基带。从发射通路来看,基带完成语音等原始信息通过AD转化等手段转化成基带信号,然后经过调制生成包含跟多有效信息,且适合信道传输的信号,最后通过射频前端将信号发射出去…...
Android SDK删除内置的触宝输入法
问题 Android 8.1.0, 展锐平台。 过CTA认证,内置的触宝输入法会连接网络,且默认就获取到访问网络的权限,没有弹请求窗口访问用户,会导致过不了认证。 预置应用触宝输入法Go版连网未明示(开启后࿰…...
[202002][Spring 实战][第5版][张卫滨][译]
[202002][Spring 实战][第5版][张卫滨][译] habuma/spring-in-action-5-samples: Home for example code from Spring in Action 5. https://github.com/habuma/spring-in-action-5-samples 第 1 部分 Spring 基础 第 1 章 Spring 起步 1.1 什么是 Spring 1.2 初始化 Spr…...
H5视频上传与播放
背景 需求场景: 后台管理系统: (1)配置中支持上传视频、上传成功后封面缩略图展示,点击后自动播放视频; (2)配置中支持上传多个文件; 前台系统: &#…...
通过OpenAI来做机械智能故障诊断-测试(1)
通过OpenAI来做机械智能故障诊断 1. 注册使用2. 使用案例1-介绍故障诊断流程2.1 对话内容2.2 对话小结3. 使用案例2-写一段轴承故障诊断的代码3.1 对话内容3.2 对话小结4. 对话加载Paderborn轴承故障数据集并划分4.1 加载轴承故障数据集并划分第一次测试4.2 第二次加载数据集自…...
ASE40N50SH-ASEMI高压MOS管ASE40N50SH
编辑-Z ASE40N50SH在TO-247封装里的静态漏极源导通电阻(RDS(ON))为100mΩ,是一款N沟道高压MOS管。ASE40N50SH的最大脉冲正向电流ISM为160A,零栅极电压漏极电流(IDSS)为1uA,其工作时耐温度范围为-55~150摄氏度。ASE40N…...
MySQL基础命令大全——新手必看
Mysql 是一个流行的开源关系型数据库管理系统,广泛用于各种 Web 应用程序和服务器环境中。Mysql 有很多命令可以使用,以下是 Mysql 基础命令: 1、连接到Mysql服务器: mysql -h hostname -u username -p 其中,"ho…...
sklearn学习-朴素贝叶斯(二)
文章目录一、概率类模型的评估指标1、布里尔分数Brier Score对数似然函数Log Loss二、calibration_curve:校准可靠性曲线三、多项式朴素贝叶斯以及其变化四、伯努利朴素贝叶斯五、改进多项式朴素贝叶斯:补集朴素贝叶斯ComplementNB六、文本分类案例TF-ID…...
MySQL_主从复制读写分离
主从复制 概述 主从复制是指将主数据库的DDL和DML操作通过二进制日志传到从库服务器中,然后在从库上对这些日志重新执行(也叫重做),从而使得从库和主库的数据保持同步。 MySQL支持一台主库同时向多台从库进行复制,从…...
shell基础学习
文章目录查看shell解释器写hello world多命令处理执行变量常用系统变量自定义变量撤销变量静态变量变量提升为全局环境变量特殊变量$n$#$* $$?运算符:条件判断比较流程控制语句ifcasefor 循环while 循环read读取控制台输入基本语法:函数系统函数basenamedirname自定义函数shel…...
考虑交叉耦合因素的IPMSM无传感器改进线性自抗扰控制策略
考虑交叉耦合因素的IPMSM无传感器改进线性自抗扰控制策略一级目录二级目录三级目录控制原理ELADRC信号提取龙格贝尔观测器方波注入simulink仿真给定转速:转速环:电流环:一级目录 二级目录 三级目录 首先声明一下,本篇博客是复现…...
2023年全国最新食品安全管理员精选真题及答案5
百分百题库提供食品安全管理员考试试题、食品安全员考试预测题、食品安全管理员考试真题、食品安全员证考试题库等,提供在线做题刷题,在线模拟考试,助你考试轻松过关。 41.《中华人民共和国食品安全法》第35条规定,以下࿰…...
git 笔记
简介 内容介绍 介绍git怎么管理和实现的 核心概念 文件名-hash-文件内容: 可以通过文件路径定位位置, 也可以通过hash定位位置;快照: 所谓一个快照其实就是一棵树, 叶子结点是一个hash,对应一个文件, 根节点对应文件夹; 一棵树就是一个快照;commit是tree, tree将文件串联, …...
ChatGPT 的盈利潜力:我使用语言模型赚取第一笔钱的个人旅程
使用 Fiverr、Python ChatGPT 和数据科学赚钱的指南。众所周知,ChatGPT 是 12 月发生的互联网突破性事件,几乎每个人都跳过了使用 AI 赚钱的潮流。在本文中,我将分享我是如何使用 ChatGPT 赚到第一笔钱的。本文包括以下主题:回到基…...
计算机网络——问答2023自用
1、高速缓冲存储器Cache的作用? 这种局部存储器介于CPU与主存储器DRAM之间,一般由高速SRAM构成,容量小但速度快,引入它是为了减小或消除CPU与内存之间的速度差异对系统性能带来的影响 (Cache可以保存CPU刚用过或循环使…...
【1247. 交换字符使得字符串相同】
来源:力扣(LeetCode) 描述: 有两个长度相同的字符串 s1 和 s2,且它们其中 只含有 字符 "x" 和 "y",你需要通过「交换字符」的方式使这两个字符串相同。 每次「交换字符」的时候&…...
边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...
基于Springboot+Vue的办公管理系统
角色: 管理员、员工 技术: 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能: 该办公管理系统是一个综合性的企业内部管理平台,旨在提升企业运营效率和员工管理水…...

