System V IPC奥秘:解锁共享内存、消息队列与信号量的高效通信之路

目录
- `🍑system V共享内存 `
- `🍒共享内存的原理`
- `共享内存数据结构`
- `查看和删除共享内存资源的命令`
- `🌻共享内存函数`
- `shmget函数`
- `ftok函数`
- `shmat函数`
- `shmdt函数`
- `shmctl函数`
- `🍃共享内存的优缺点`
- `🦔system V消息队列(了解)`
- `消息队列的相关操作接口`(相关接口的参数与共享内存类似)
- `🦅system V信号量 sem`
- `📚信号量理论`
- `信号量的数据结构:`
- `信号量的相关操作接口`
- `指令查找以及删除信号量:`
🍑system V共享内存
共享内存
是最快
的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到
内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。
🍒共享内存的原理
OS
在内存上开辟一段空间,然后与进程构建映射(通过页表),映射到堆栈之间的共享区
中,将该空间的起始地址(虚拟地址)返回给用户,该进程就可以通过虚拟地址访问该内存了。
其中,开辟空间和构建映射关系,需要修改进程地址空间和页表,都是内核数据结构,都是OS做的,所以OS会提供相应的系统调用,如果通过同样的系统调用,就也可以将该空间映射到另一个进程的共享区中,也可以使用虚拟地址访问该内存,所以就让不同的进程看到了同一个内存资源。

共享内存数据结构
一个时刻,在系统中可能存在很多个进程在进行通信,就可能存在很多个共享内存,OS就会将这些共享内存进行管理,先描述再组织。下面是描述共享内存的数据结构
。
struct shmid_ds {struct ipc_perm shm_perm; /* operation perms */int shm_segsz; /* size of segment (bytes) */__kernel_time_t shm_atime; /* last attach time */__kernel_time_t shm_dtime; /* last detach time */__kernel_time_t shm_ctime; /* last change time */__kernel_ipc_pid_t shm_cpid; /* pid of creator */__kernel_ipc_pid_t shm_lpid; /* pid of last operator */unsigned short shm_nattch; /* no. of current attaches */unsigned short shm_unused; /* compatibility */void *shm_unused2; /* ditto - used by DIPC */void *shm_unused3; /* unused */
};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 */
};
共享内存存在很多个,那么怎么保证两个进程看到的是同一个共享内存呢?
key_t
:共享内存的唯一值。- 我们只需要两个进程,在使用 ftok 形成key时,使用同样的参数即可。
共享内存,如果进程结束,没有主动释放,则会一直存在,除非重启系统。共享内存的生命周期随内核
。
查看和删除共享内存资源的命令
ipcs -m
:查看系统中指定用户创建的共享内存。

ipcrm -m shmid
:删除指定shmid的共享内存。
🌻共享内存函数
shmget函数
功能:用来创建共享内存。
int shmget(key_t key, size_t size, int shmflg);
参数:
- key:这个共享内存段名字 :共享内存在内核中唯一性的标识。
- size:共享内存大小。在内核中,共享内存的大小是以
4KB为基本单位
的。如果size设置为4097,则实际开辟的是8KB。 - shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的。
- 返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1。
常用shmflg选项
:
- IPC_CREAT:如果创建的共享内存不存在,就创建,如果存在,直接获取它。
- IPC_CREAT:单独使用没有意义。
- IPC_CREAT | IPC_CREAT:如果创建的共享内存不存在,就创建,如果存在,就会出错返回。
- 可以
指定该共享内存的权限
,直接 或上权限即可(如 |0666)。
ftok函数
功能:生成一个System V IPC key
key_t ftok(const char *pathname, int proj_id);
用法:传入路径+文件名(随便一个字符串也行)和 一个整型,生成一个System V IPC key
.
返回值:成功返回生成的key,失败返回-1,错误码被设置。
shmat函数
功能:将共享内存段连接到进程地址空间(建立映射)
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数:
- shmid:共享内存标识。
- shmaddr:指定连接的地址(虚拟地址)。
- shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY。
- 返回值:成功返回一个指针,指向共享内存第一个字节;失败返回-1。
说明:
shmaddr 为 NULL
,核心自动选择一个地址。
shmaddr
不为 NULL
且smflg 无 SHM_RND标记
,则以shmaddr为连接地址。
shmaddr
不为 NULL
且shmflg 设置了 SHM_RND标记
,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr -
(shmaddr % SHMLBA)。
shmflg = SHM_RDONLY
,表示连接操作用来只读共享内存。
shmdt函数
功能:将共享内存段与当前进程脱离(去关联)。
int shmdt(const void *shmaddr);
参数:
- shmaddr:由shmat所返回的指针。
- 返回值:成功返回0;失败返回-1。
注意:
将共享内存段与当前进程脱离不等于删除共享内存段。
shmctl函数
功能:用于控制共享内存。
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数:
- shmid:由
shmget
返回的共享内存标识码。 - cmd:将要采取的动作(有三个可取值)。
- buf:指向一个保存着共享内存的模式状态和访问权限的数据结构(如果不关心,直接设置为nullptr)。
- 返回值:成功返回0;失败返回-1。

🍃共享内存的优缺点
-
共享内存优点:
是进程间通信中速度最快的。(管道通信中,进程需要通过系统调用往管道里面读写数据,两个进程通信一次,至少两次系统调用;而共享内存,进程可以通过共享内存的起始虚拟地址,直接访问到内存,一个进程向共享内存中写了数据,另一个进程就可以直接看到,只需要一次系统调用即可)。 -
共享内存缺点:
没有提供进程间任何的协同机制,如果需要,可以自己实现。有些场景不适用,比如我们一个进程发送一个数据,如果没有协同机制,那么可能该进程数据才写一半,另一个进程就读取了,会造成数据不一致。- 可以通过管道来实现同步。示例代码中有体现。
示例代码演示:
Comm.hpp
#pragma once
#include <iostream>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <string>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>using namespace std;const char *pathname = "/home/whb";
const int proj_id = 0x66;// 在内核中,共享内存的大小是以4KB为基本单位的. 你只能用你申请的大小。建议申请大小是n*4KB
const int defaultsize = 4096; // 单位是字节std::string ToHex(key_t k){char buffer[1024];snprintf(buffer, sizeof(buffer), "0x%x", k);return buffer;
}key_t GetShmKeyOrDie(){key_t k = ftok(pathname, proj_id);if (k < 0) {std::cerr << "ftok error, errno : " << errno << ", error string: " << strerror(errno) << std::endl;exit(1);}return k;
}int CreateShmOrDie(key_t key, int size, int flag){int shmid = shmget(key, size, flag);if (shmid < 0){std::cerr << "shmget error, errno : " << errno << ", error string: " << strerror(errno) << std::endl;exit(2);}return shmid;
}int CreateShm(key_t key, int size){// IPC_CREAT: 不存在就创建,存在就获取// IPC_EXCL: 没有意义// IPC_CREAT | IPC_EXCL: 不存在就创建,存在就出错返回return CreateShmOrDie(key, size, IPC_CREAT | IPC_EXCL | 0666);
}int GetShm(key_t key, int size){return CreateShmOrDie(key, size, IPC_CREAT);
}void DeleteShm(int shmid){int n = shmctl(shmid, IPC_RMID, nullptr);if (n < 0){std::cerr << "shmctl error" << std::endl;}else{std::cout << "shmctl delete shm success, shmid: " << shmid << std::endl;}
}void ShmDebug(int shmid){struct shmid_ds shmds;int n = shmctl(shmid, IPC_STAT, &shmds);if (n < 0){std::cerr << "shmctl error" << std::endl;return;}std::cout << "shmds.shm_segsz: " << shmds.shm_segsz << std::endl;std::cout << "shmds.shm_nattch:" << shmds.shm_nattch << std::endl;std::cout << "shmds.shm_ctime:" << shmds.shm_ctime << std::endl;std::cout << "shmds.shm_perm.__key:" << ToHex(shmds.shm_perm.__key) << std::endl;
}void *ShmAttach(int shmid){void *addr = shmat(shmid, nullptr, 0);if ((long long int)addr == -1){std::cerr << "shmat error" << std::endl;return nullptr;}return addr;
}void ShmDetach(void *addr){int n = shmdt(addr);if (n < 0){std::cerr << "shmdt error" << std::endl;}
}
Fofi.hpp
#ifndef __COMM_HPP__
#define __COMM_HPP__#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <cassert>using namespace std;#define Mode 0666
#define Path "./fifo"class Fifo
{
public:Fifo(const string &path = Path) : _path(path){umask(0);int n = mkfifo(_path.c_str(), Mode);if (n == 0){cout << "mkfifo success" << endl;}else{cerr << "mkfifo failed, errno: " << errno << ", errstring: " << strerror(errno) << endl;}}~Fifo(){int n = unlink(_path.c_str());if (n == 0){cout << "remove fifo file " << _path << " success" << endl;}else{cerr << "remove failed, errno: " << errno << ", errstring: " << strerror(errno) << endl;}}
private:string _path; // 文件路径+文件名
};class Sync{
public:Sync() : rfd(-1), wfd(-1){}void OpenReadOrDie(){rfd = open(Path, O_RDONLY);if (rfd < 0)exit(1);}void OpenWriteOrDie(){wfd = open(Path, O_WRONLY);if (wfd < 0)exit(1);}bool Wait(){bool ret = true;uint32_t c = 0;ssize_t n = read(rfd, &c, sizeof(uint32_t));if (n == sizeof(uint32_t)){std::cout << "server wakeup, begin read shm..." << std::endl;}else if (n == 0){ret = false;}else{return false;}return ret;}void Wakeup(){uint32_t c = 0;ssize_t n = write(wfd, &c, sizeof(c));assert(n == sizeof(uint32_t));std::cout << "wakeup server..." << std::endl;}~Sync() {}
private:int rfd;int wfd;
};#endif
ShmClient.cc
#include "Comm.hpp"
#include "Fifo.hpp"
#include <unistd.h>
int main()
{key_t key = GetShmKeyOrDie();std::cout << "key: " << ToHex(key) << std::endl;// sleep(2);int shmid = GetShm(key, defaultsize);std::cout << "shmid: " << shmid << std::endl;// sleep(2);char *addr = (char *)ShmAttach(shmid);std::cout << "Attach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;// sleep(5);memset(addr, 0, defaultsize);Sync syn;syn.OpenWriteOrDie();// 可以进行通信了for (char c = 'A'; c <= 'Z'; c++) // pipe, fifo, ->read/write->系统调用, shm -> 没有使用系统调用!!{addr[c - 'A'] = c;sleep(1);syn.Wakeup();}ShmDetach(addr);std::cout << "Detach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;sleep(5);return 0;
}
Shm.Server.cc
#include "Comm.hpp"
#include "Fifo.hpp"
#include <unistd.h>int main(){// 1. 获取keykey_t key = GetShmKeyOrDie();std::cout << "key: " << ToHex(key) << std::endl;// sleep(2);// 2. 创建共享内存int shmid = CreateShm(key, defaultsize);std::cout << "shmid: " << shmid << std::endl;// sleep(2);// ShmDebug(shmid);// 4. 将共享内存和进程进行挂接(关联)char *addr = (char *)ShmAttach(shmid);std::cout << "Attach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;// 0. 先引入管道Fifo fifo;Sync syn;syn.OpenReadOrDie();// 可以进行通信了for(;;){if(!syn.Wait()) break;cout << "shm content: " << addr << std::endl;}ShmDetach(addr);std::cout << "Detach shm success, addr: " << ToHex((uint64_t)addr) << std::endl;// 3. 删除共享内存DeleteShm(shmid);return 0;
}
🦔system V消息队列(了解)
消息队列
提供了一个从一个进程向另外一个进程发送一个有类型的数据块
的方法。- 每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值。
- 特性方面
- IPC资源必须删除,否则不会自动清除,除非重启,所以
system V IPC资源的生命周期随内核。
- IPC资源必须删除,否则不会自动清除,除非重启,所以
进程A与进程B,其中,进程A可以向内核中发送和读取数据块,进程B也可以发送和读取,发送的数据块放到一个队列中,当进程A在发送的时候,另一个进程也可以发送,数据块中有标记位标记是谁发送的。
消息队列的数据结构:
struct msqid_ds {struct ipc_perm msg_perm; /* Ownership and permissions */time_t msg_stime; /* Time of last msgsnd(2) */time_t msg_rtime; /* Time of last msgrcv(2) */time_t msg_ctime; /* Time of creation or lastmodification by msgctl() */unsigned long msg_cbytes; /* # of bytes in queue */msgqnum_t msg_qnum; /* # number of messages in queue */msglen_t msg_qbytes; /* Maximum # of bytes in queue */pid_t msg_lspid; /* PID of last msgsnd(2) */pid_t msg_lrpid; /* PID of last msgrcv(2) */
};
struct ipc_perm {key_t __key; /* Key supplied to msgget(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 */unsigned short __seq; /* Sequence number */
};
消息队列的相关操作接口
(相关接口的参数与共享内存类似)
-
创建消息队列
int msgget(key_t key,int msgflg);
-
删除消息队列
int msgctl(int msqid,int cmd,struct msqid_ds *buf);
-
向队列里发送数据
int msgsnd(int msqid,const void* msgp,size_t msgsz,int msgflq);
-
读取数据
ssize_t msgrcv(int msqid,void* msgp,size_t msgsz,long msgtyp,int msgflg);
-
查找消息队列的指令
ipcs -q
-
删除消息队列的指令
ipcrm -q msqid
🦅system V信号量 sem
信号量
主要用于同步
和互斥
的,下面先来看看什么是同步和互斥。
- 进程互斥 ·
- 由于各进程要求共享资源,而且有些资源需要互斥使用,因此各进程间竞争使用这些资源,进程的这种关系为
进程的互斥
。 - 系统中某些资源一次只允许一个进程使用,称这样的资源为
临界资源
或互斥资源。 - 访问临界资源的代码段叫
临界区
。
- 由于各进程要求共享资源,而且有些资源需要互斥使用,因此各进程间竞争使用这些资源,进程的这种关系为
- 特性方面
- IPC资源必须删除,否则不会自动清除,除非重启,所以system V IPC资源的生命周期
随内核
。
- IPC资源必须删除,否则不会自动清除,除非重启,所以system V IPC资源的生命周期
📚信号量理论
信号量(信号灯)
- 对于一份内存资源(临界资源),如果只允许整体被使用的话,每一次只允许一个进程访问,这种工作做方式效率低。
- 其中,
信号量
就提供了一种机制,将一份内存资源拆分成很多小资源,允许多个进程同时访问,前提:每一个进程访问的是不同的被切分的小资源,就能做到真正的并发访问。
在这种情况下,只需要1.
限制进来的进程数。2.
合理分配资源 即可。
信号量
的本质
是一个计数器,是描述临界资源数量的计数器
。(如下图,假设计数器 int count = 9;)
在访问资源的时候,会经历一下几个步骤:
- 申请信号量,if count>0,则申请成功,并且会将count–,也叫做
p操作
; - 访问临界资源。
- 释放信号量,count++,叫做
v 操作
;

在多进程场景,int不能实现信号量得效果,原因如下:
- count
++/--不是原子的
,所以在++/–过程中,可能有其他进程也会申请信号量,会造成访问冲突问题。(比如现在count=1,然后一个进程来申请,然后再count–过程中,这时count还是1,又有另一个进程来申请信号量,又会count–,count–完成后,就变为成-1了)。 无法在进程间共享
,变量在进程中做修改时,会发生写时拷贝
。所以须让不同的进程先看到同一份资源—计数器资源!所以信号量属于进程间通信。
所有进程,访问临界资源,都必须先申请信号量–所有的进程都得先看到一个信号量–所以信号量本身就是共享资源!所以信号量的申请(++)和释放(–)都必须是原子的。pv操作必须是原子的。
如果信号量的初始值是1,就是互斥,也就是二元信号量,也是后面文章会说到的 锁。
信号量的数据结构:
struct semid_ds {struct ipc_perm sem_perm; /* Ownership and permissions */time_t sem_otime; /* Last semop time */time_t sem_ctime; /* Creation time/time of lastmodification via semctl() */unsigned long sem_nsems; /* No. of semaphores in set */
};
struct ipc_perm {key_t __key; /* Key supplied to semget(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 */unsigned short __seq; /* Sequence number */
};
信号量的相关操作接口
-
申请信号量
int semget(key_t key, int nsems, int semflg);
-
控制信号量
int semid, int semnum, int cmd, ...);
-
pv操作
int semop(int semid, struct sembuf *sops, size_t nsops);
指令查找以及删除信号量:
- 查找:
ipcs - s
- 删除:
ipcrm -s semid
共享内存 信号量 消息队列 是OS系统特意设计的 system V进程间通信的。
共享内存 信号量 消息队列可以看成同一种资源,IPC资源,OS注定要对IPC资源进程管理–先描述再组织。
我们可以发现,在内核中,无论是共享内存 ,信号量 还是消息队列,描述他们的结构体里的第一个成员都是 kern ipc_perm类型的。在内核中,有一个数据结构ipc_id_ary,里面第一个成员是size,其余都是类似于指针数组,类型为 kern ipc_perm*,当系统创建了一个IPC 资源时,会将该资源的数据结构的第一个成员地址存放在该kern ipc_perm*数组中,这样,系统中的每一个IPC
资源都组织起来了,方便管理。类似于C++中的多态。

相关文章:

System V IPC奥秘:解锁共享内存、消息队列与信号量的高效通信之路
🍑个人主页:Jupiter. 🚀 所属专栏:Linux从入门到进阶 欢迎大家点赞收藏评论😊 目录 🍑system V共享内存 🍒共享内存的原理共享内存数据结构查看和删除共享内存资源的命令 🌻共享内存…...

怎么将pdf转为ppt文件?pdf转ppt的8个方法
在诸多职场与学术交流的场合中,我们时常面临将详尽的PDF文件转化为生动且易于编辑的PPT演示文稿的需求。这一转换不仅是为了满足演示时的灵活性,更是为了提升信息传递的效率与观众的理解度。从简单的在线工具到功能全面的专业软件,我们拥有多…...

【Datawhale AI夏令营第四期】 魔搭-大模型应用开发方向笔记 Task03 大咖项目分享 人话八股文Bakwaan_Buddy项目开发尝试
【Datawhale AI夏令营第四期】 魔搭-大模型应用开发方向笔记 Task03 人话八股文Bakwaan_Buddy项目开发尝试 Idea: 我们草台班子目前的想法是解决大家计算机学院毕业面临的BUG——不爱背、背不下来八股文,觉得枯燥、烦、工作了用不着,反正就是知识他不进…...

如何在wordpress当中使用插件WP Coder(将html、css、javascript应用到wordpress上)
了解认识阶段 安装并运行好WP Coder之后如下图: 设置全局PHP 禁用gutenberg 输入代码 add_filter(gutenberg_can_edit_post, __return_false, 10); add_filter(use_block_editor_for_post, __return_false, 10); 记得点击save并勾选enable PHP code 禁用之后打…...
ActiveMQ、RabbitMQ、Kafka、RocketMQ在消息回溯、消息堆积+持久化、消息追踪、消息过滤的区别
ActiveMQ、RabbitMQ、Kafka、RocketMQ在消息回溯、消息堆积持久化、消息追踪、消息过滤等方面各有其独特的特点和优势。以下是这四个方面的详细比较: 1. 消息回溯 ActiveMQ:支持消息回溯功能。ActiveMQ可以将消息持久化到磁盘上,因此当需要…...

使用ITextRenderer导出PDF后无法打开问题,提示‘无法打开此文件‘
依赖如下 <!-- https://mvnrepository.com/artifact/org.xhtmlrenderer/flying-saucer-pdf --> <dependency><groupId>org.xhtmlrenderer</groupId><artifactId>flying-saucer-pdf</artifactId><version>9.1.22</version> &l…...
STL必须掌握的几大常见算法
1. std::sort 功能:对容器中的元素进行排序。 用法: #include <algorithm> #include <vector>std::vector<int> vec = {3, 1, 4, 1, 5, 9}; std::sort(vec.begin(), vec.end());你知道sort内部用的什么排序吗 2. std::reverse 功能:将容器中的元素顺…...

HAproxy 七层负载均衡调度器详解及配置
HAproxy 七层负载均衡 负载均衡技术 负载均衡(Load Balance):一种服务,或基于硬件设备实现的高可用的反向代理技术,是指将特定的业务流量分摊给一个或多个后端的特定服务器或设备,实现高并发处理业务流量…...
Python学习笔记--私有属性、构造方法、析构方法、常用内置方法
目录 私有属性 构造方法 析构方法 常用内置方法 私有属性 1. 私有属性是指在类内可以直接访问、而在类外无法直接访问的属性 2. Python中规定,在定义类时,如果一个类属性名是以__(两个下划线)开头,则该类属性为私…...
4章8节:用R做数据重塑,行列命名和数据类型转换
在R语言中,行列命名和数据类型转换是数据处理中的两个基础性操作。它们不仅对数据的可读性和组织性至关重要,而且在执行数据分析、模型构建和结果解释时也扮演着重要的角色。 一、行和列命名 在数据科学和统计分析中,命名是组织和管理数据的一个重要部分。尤其是在处理复杂…...
浏览器发出请求到响应的过程
客户端发起请求:用户输入URL并回车,浏览器解析URL,生成HTTP请求。 DNS解析:浏览器查看本地hosts文件是否有域名的IP地址映射,如果没有则向DNS服务器发起解析请求,获取域名对应的IP地址。 建立TCP连接&…...

eNSP 华为划分VLAN
SW1: <Huawei>system-view [Huawei]sysname SW1 [SW1]VLAN batch 10 20 //批量划分vlan [SW1]interface GigabitEthernet0/0/1 [SW1-GigabitEthernet0/0/1]port link-type access //设置为access口,access口允许单个vlan通过 [SW1-GigabitEth…...

公用事业公司签署大规模电力供应协议
随着人工智能技术的迅猛发展,美国公用事业公司与数据中心运营商之间的电力供应协议数量显著增加,为未来几季度的销售和利润增长奠定了基础。根据高盛今年5月发布的一份报告,到2030年,数据中心的发电量预计将占美国总发电量的8%&am…...

C语言 | Leetcode C语言题解之第341题扁平化嵌套列表迭代器
题目: 题解: struct NestedIterator {int *vals;int size;int cur; };void dfs(struct NestedIterator *iter, struct NestedInteger **nestedList, int nestedListSize) {for (int i 0; i < nestedListSize; i) {if (NestedIntegerIsInteger(neste…...

冷知识:编程第一人是位伟大的女性
冷门智慧:阿达编程先驱的传奇人生揭秘在线播放免费听 - 喜马拉雅手机版欢迎收听由主播壹道徽为您带来的“冷门智慧:阿达编程先驱的传奇人生揭秘”精彩有声内容,该音频时长5分18秒,已被收听1062次,用户嘎嘎呗嘎嘎评价说…...

Python爬虫使用实例
IDE:大部分是在PyCharm上面写的 解释器装的多 → 环境错乱 → error:没有配置,no model 爬虫可以做什么? 下载数据【文本/二进制数据(视频、音频、图片)】、自动化脚本【自动抢票、答题、采数据、评论、点…...
主成分分析(PCA)
1 主成分分析简介 主成分分析(Principal Component Analysis,PCA), 将多个变量通过线性变换以选出较少个数重要变量的一种多元统计分析方法。主成分分析是由卡尔皮尔逊(Karl Pearson)于1901年发明的。通过维度约减的方式将高维度…...
python实现生命游戏
“生命游戏”(Conway’s Game of Life)是由数学家约翰康威提出的一种零玩家游戏。它是一个细胞自动机,由一组简单的规则决定每个细胞的状态。以下是用Python实现“生命游戏”的代码示例: Python代码实现 import numpy as np imp…...

基于vue框架的CIA报价平台的设计与实现1xv02(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。
系统程序文件列表 项目功能:用户,供应商,产品分类,产品信息,在线咨询,资质申请 开题报告内容 基于Vue框架的CIA报价平台的设计与实现 开题报告 一、选题背景 随着市场竞争的日益激烈,企业对于成本控制与效率提升的需求愈发迫切。在采购与供应链管理…...
【Kubernetes】k8s集群Pod控制器
目录 一.Pod控制器作用 二.Pod控制器类型 1.Deployment(简称deploy) ReplicaSet(简称rs) 2.StatefulSet(简称sts) 创建SatefulSet控制器 3.DaemonSet(简称ds) 4.Job 5.Cron…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...

NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...
MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用
文章目录 一、背景知识:什么是 B-Tree 和 BTree? B-Tree(平衡多路查找树) BTree(B-Tree 的变种) 二、结构对比:一张图看懂 三、为什么 MySQL InnoDB 选择 BTree? 1. 范围查询更快 2…...

Rust 开发环境搭建
环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行: rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu 2、Hello World fn main() { println…...

华为OD机试-最短木板长度-二分法(A卷,100分)
此题是一个最大化最小值的典型例题, 因为搜索范围是有界的,上界最大木板长度补充的全部木料长度,下界最小木板长度; 即left0,right10^6; 我们可以设置一个候选值x(mid),将木板的长度全部都补充到x,如果成功…...

消防一体化安全管控平台:构建消防“一张图”和APP统一管理
在城市的某个角落,一场突如其来的火灾打破了平静。熊熊烈火迅速蔓延,滚滚浓烟弥漫开来,周围群众的生命财产安全受到严重威胁。就在这千钧一发之际,消防救援队伍迅速行动,而豪越科技消防一体化安全管控平台构建的消防“…...