网站 根目录 虚拟目录/网页推广平台

目录
- `🍑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…...

什么是NLP实体识别?
目录 1. 实体识别的基本概念 1.1 什么是实体识别? 1.2 实体识别的应用场景 2. 实体识别的常用方法 2.1 基于规则的方法 2.1.1 规则定义与模式匹配 2.2 基于机器学习的方法 2.2.1 特征工程与传统机器学习模型 2.3 基于深度学习的方法 2.3.1 神经网络模型 …...

掌握Jenkins自动化部署:从代码提交到自动上线的全流程揭秘
Jenkins自动化部署是现代软件开发中不可或缺的一部分,它不仅简化了代码的发布过程,还为整个团队带来了无与伦比的效率和协作力。想象一下,开发者们可以专注于编写高质量的代码,而不是为繁琐的手动部署所烦恼;测试人员能…...

糟糕界面集锦-控件篇09
目前我们还无法确定该把这个问题划到哪个类别中,但是如图所示,在一个列表框中只显示3 个项目无疑是愚蠢的。 这是微软的文件管理器提供的文件关联界面,用户需要把某一个特定类型的文件与一个应用程序关联时会弹出该对话框。如示例:…...

喵喵蓝牙热敏打印机(下)
目录 前言一、电量、温度、缺纸检测1.电量检测2.针头温度检测3.缺纸检测 二、蓝牙APP通信打印1.蓝牙初始化2.APP通信打印 三、FreeRTOS任务整合 前言 喵喵蓝牙热敏打印机(上) 内容有点多,就分为了上下两篇。 一、电量、温度、缺纸检测 先启…...

软件测试第1章 软件测试是什么
目录 内容说明 一、软件测试与质量概览需要熟悉什么 二、如何理解质量保证 三、软件测试的误区-程序员和测试的关系 四、软件测试是什么? 五、软件测试的目的 六、软件测试与软件质量保证 七、软件测试的必要性 八、软件测试的基本概念分析 …...

【技术分享】 hysteria2从服务端到客户端部署教程
hysteria2从服务端到客户端部署教程 前言 在如今的网络环境中,尤其是涉及跨国访问的场景中,hysteria2作为一个新兴的传输协议工具,凭借其高效的传输能力和灵活的配置方式,受到了越来越多用户的青睐。本教程将带您一步步完成hyst…...

C++入门基础知识16
C 的关键字(接上一篇博文!!!) 54. typeid 指出指针或引用指向的对象的实际派生类型。 55. typename typename(类型名字)关键字告诉编译器把一个特殊的名字解释成一个类型。在下列情况下必须对一…...

浏览器调试工具-Chrome Dev Tools
浏览器调试模式下的各个调试工具是常用的工具集,能够帮助开发者理解、调试和优化网页。 1.打开方式 直接在浏览器中按下F12键右键点击页面上的任一元素,选择“检查”(Inspect)在浏览器右上角点击菜单按钮,选择“更多…...

基于车联网大数据平台的用户驾驶习惯行为画像分析
近年来,新能源汽车行业的迅速发展推动了汽车智能化的趋势。新能源汽车上配备了成千上万的传感器,这些传感器采集了大量的行车数据被用于车辆运行状况的监控与分析。另一方面,采集到的大量行车数据,也能很好地体现用户的驾驶习惯。…...

Ubuntu24.04搭建maxkb开发环境
接上文:windows10搭建maxkb开发环境(劝退指南) 上文在windows10环境搭建maxkb开发环境遇到各种坑,后面就转战ubuntu平台,果然比较顺利的完成开发环境搭建。当然遇到相关的问题还是可以参考上文《windows10搭建maxkb开发…...