当前位置: 首页 > news >正文

Linux 进程间通信 共享内存_消息队列_信号量

共享内存

共享内存是一种进程间通信(IPC)机制,它允许多个进程访问同一块内存区域。这种方法可以提高效率,因为数据不需要在进程之间复制,而是可以直接在共享的内存空间中读写。

 使用共享内存的步骤通常包括:

  1. 创建共享内存:一个进程shmget()创建共享内存区域。
  2. 映射共享内存:其他进程将该共享内存映射到自己的地址空间中的共享区中。
  3. 读写数据:进程可以在共享内存中读写数据,进行通信。
  4. 解除映射和删除:使用完后,进程解除映射并清理共享内存。

shmget()创建共享内存 获取标识符

int shmget(key_t key, size_t size, int shmflg);

#include <sys/shm.h>

参数

  • key: 一个唯一的标识符,用于区分不同的共享内存段。可以使用 ftok 函数生成。
  • size: 请求的共享内存段的大小(以字节为单位)。
  • shmflg: 位图 控制标志,用于指定权限和其他选项。常见的选项包括:
    • IPC_CREAT: 如果共享内存段不存在则创建它。如果存在就获取它
    • IPC_EXCL| IPC_CREAT :一起使用,表示如果共享内存段不存在就创建它,反之,存在则返回错误。(返回成功一定是创建新的共享内存)
    • 权限标志,例如 0666 表示可读可写。

返回值

  • 成功时,返回共享内存标识符(非负整数)。(相当于FILE*)
  • 失败时,返回 -1,并设置 errno 以指示错误类型。

key作为标识符但不能由系统创建分配给进程,而是由用户自己创建并给进程,但要确保key是唯一的。

我们知道key是不同进程找到同一个内存空间的关键,要确保它们的key是一样的。如果是系统分配fey,A进程先创建了共享内存获得一个keg,B进程如果想访问A创建的共享内存,就要获取到key,但A B进程相互独立不可能从A进程中获取到key。

但如果是用户给,可以让用户先生成唯一的key,再把key作为全局变量写在AB进程的源代码中,这样AB进程就可以通过同一个key访问到同一个共享内存了。

如何生成唯一的key?

ftok()

key_t ftok(const char *pathname, int proj_id);
key_t key = ftok("/path/to/file", 'R'); // 'R' 是一个项目标识符

ftok()函数是一个用于生成唯一键值的系统调用,它接受一个文件路径和一个项目标识符(也可以是数字,通常是一个字符),并返回一个唯一的key。这个方法基于文件的 inode 号,因此只要文件存在且未被删除,返回的key就会是唯一的。

ipcs -m命令 显示所有的共享内存信息

ipcs -m 用于显示系统中当前存在的共享内存段的信息。

ipcs -m
key        shmid    owner    perms      bytes      nattch    status
0x12345678  12345   user     666        1024       2

这条命令会列出所有的共享内存段,包括它们的 shmid、键值、大小和其他信息。

bytes 1024 操作系统申请空间是按块为单位(4kb...)来申请空间的,4096*x。假设该操作系统块的大小为4kb如果你申请4097字节,还是会申请8kb空间,但只让你用4097字节的空间,越界就报错。

ipcrm -m命令删除共享内存段

ipcrm -m shmid

shmctl() 控制共享内存 IPC_RMID删除

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

参数

  • shmid:共享内存段的标识符,通常是通过 shmget 函数获取的。
  • cmd:命令,用于指定要执行的操作,可以是以下常用值之一:
    • IPC_STAT:将共享内存段的信息填充到 buf 指向的结构中。
    • IPC_RMID:标记共享内存段以便删除。
    • SHM_LOCK:锁定共享内存段。
    • SHM_UNLOCK:解锁共享内存段。
    • SHM_INFO:获取共享内存的统计信息(POSIX扩展)。
  • buf:指向 shmid_ds 结构的指针,用于存储共享内存的元数据。
  • 成功时返回 0。
  • 失败时返回 -1,并设置 errno 以指示错误原因。

shmat() 将共享内存挂接到自己的地址空间中

void* shmat(int shmid, const void* shmaddr, int shmflg);

参数说明

  • shmid: 共享内存段的标识符,通常通过 shmget() 获取。
  • shmaddr: 指定共享内存挂接到虚拟地址空间的起始地址。如果为 NULL,系统会选择一个合适的地址。
  • shmflg: 标志位: 0默认附加模式,进程可以读写共享内存。 SHM_RDONLY: 只读模式,进程只能读取数据,不能写入。

返回值

  • 成功时,返回指向共享内存挂接到虚拟地址空间的起始地址
  • 失败时,返回 (void*) -1,并设置 errno 指示错误类型。

注意共享内存也是有权限的,进程如果没有对应的权限是不能完成挂接的。

shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0666); // 创建共享内存,权限为可读可写

在shmget()创建共享内存时,我们就可以设置权限0666:所有用户都可以读写。

如果不设置权限,默认权限是0600:创建者可读写 其他人无权限

shmdt()取消挂载

int shmdt(const void *shmaddr);

const void *shmaddr:挂载到进程的虚拟地址空间的起始地址 

shmdt()于从进程的地址空间分离已经附加的共享内存段。它不会删除共享内存段本身。

shmctl(IPC_RMID) :用于标记共享内存段为删除,等待所有附加的进程分离后释放资源。

共享内存通信速度最快

1.直接访问:共享内存允许多个进程直接访问同一块内存区域,避免了数据复制的开销。这与其他通信方式(如管道、消息队列等)不同,后者通常需要在进程之间复制数据

管道:数据从外设读取到进程A的内核空间,然后通过系统调用将数据写入管道。B通过系统调用从管道中读取数据,再将其拷贝到自己的内存中。

这种方式总共涉及四次拷贝:一次从外设到进程A的内核空间,一次从内核空间写入管道,一次从管道读取到进程B的内核空间,最后一次从内核空间到进程B的内存。

共享内存 :数据从外设读取到内核空间后,进程A和进程B可以直接访问共享的内存区域,避免了多次拷贝。

因此,总的拷贝次数减少为两次:一次从外设读取到内核空间,另一次是进程B直接从共享内存读取数据。

2.低延迟:由于不涉及操作系统内核的上下文切换,共享内存通信的延迟较低,特别适合需要高频率、低延迟的数据交换的场景。

3.高吞吐量:共享内存能够支持大量数据的快速传输,适合处理大规模数据或高并发的情况。

4.减少系统调用:其他通信机制往往需要进行系统调用,而共享内存可以减少这种需求,从而提升性能。

消息队列

os提供一个队列,A B进程都可以看到这个队列,把结构体struct data作为结点放入队列,再让另一个进程拿该节点,实现通信。

怎么知道这个节点是其他进程放的呢?
用户要自己创建struct data,里面有int type标识符,定义A进程标识符1 B为2,这样就可以区分拿与自己标识符不同的节点。

msgget() msgctl() 

这两个函数用法和shmget() shmctl() 差不多

int msgget(key_t key, int msgflg);

参数说明:

  • key: 消息队列的键值,可以通过 ftok() 函数生成。
  • msgflg: 控制消息队列的行为,通常使用以下标志:
    • IPC_CREAT: 如果消息队列不存在则创建一个。
    • IPC_EXCL: 如果消息队列已经存在,调用失败。
    • 权限位(如 0666)控制访问权限。

返回值:

  • 成功时返回消息队列的标识符(非负整数)。
  • 失败时返回 -1。
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

参数说明:

  • msqid: 消息队列的标识符。
  • cmd: 控制命令,可以是以下之一:
    • IPC_STAT: 获取消息队列的状态信息。
    • IPC_SET: 设置消息队列的属性。
    • IPC_RMID: 删除消息队列。
  • buf: 指向 msqid_ds 结构的指针,用于存放或设置属性。

返回值:

  • 成功时返回 0。
  • 失败时返回 -1。

ipcs -q  ipcrm -m

ipcs -q 用于显示当前系统中所有的消息队列的信息。

------ Message Queues --------
key        msqid      owner      perms      used-bytes
0x12345678  12345      user1     666        0
0x87654321  12346      user2     666        0

ipcrm -m删除共享内存段

ipcrm -m <shmid>

msgsnd() msgrcv() 特有

msgsnd() msgrcv()是消息队列特有的函数

msgsnd() 发送消息

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

参数说明:

  • msqid: 消息队列的标识符。
  • msgp: 指向要发送的消息节点的指针,消息的结构体struct msgbuf要以标识符long mtype开头 
  • msgsz: 消息内容的字节数(不包括 mtype)。
  • msgflg: 自己的标识符,常用值包括 IPC_NOWAIT(如果队列已满则不等待,直接返回错误)。0: 默认行为,发送或接收消息时会阻塞,直到操作成功。

返回值:

  • 成功时返回 0。
  • 失败时返回 -1。

msgrcv() 接收消息

int msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

参数说明:

  • msqid: 消息队列的标识符。
  • msgp: 指向接收消息的缓冲区的指针。指向struct data
  • msgsz: 消息内容的最大字节数(不包括 mtype)。
  • msgtyp: 指定接收消息的标识符mtype,可以是特定类型的消息(如 1),也可以是 0(接收任何类型的消息)或 -1(接收队列中最早的消息)。
  • msgflg: 控制接收行为的标志,常用值包括 IPC_NOWAIT(如果队列为空则不等待,直接返回错误)。

返回值:

  • 成功时返回接收到的消息字节数。
  • 失败时返回 -1。

信号量

信号量是一种用于进程间同步和互斥的机制,主要用于控制多个进程对共享资源的访问。它通过维护一个整型计数器来实现,计数器的值表示可用资源的数量或状态。

我们之前学过管道,我们把管道的资源看作一个整体,要写整个资源所有地方都可以写,要读都可以读。

现在我们把整个资源,分成多份,一个进程访问一份。整个资源,有部分可能在写入,还有部分可能在读,不同进程,访问共享资源,有一定并发性。

1.但是我们怎么知道整个资源里面还有多少份没有进程占用?

我们可以用一个计算器count来记录里面剩余个数,count=16 每进入一个进程count--(这种操作称作P操作),出去count++(V操作)。

  1. P操作(等待)

    • 当线程或进程希望访问某个共享资源时,它会执行P操作。这个操作的作用是检查信号量的值:
      • 如果信号量的值大于0,表示资源可用,线程可以继续执行,并且信号量的值减1。
      • 如果信号量的值为0,表示资源不可用,线程会被阻塞,直到信号量的值变为正。
  2. V操作(释放)

    • 当线程或进程完成对共享资源的使用后,会执行V操作。这个操作的作用是将信号量的值增加1,表示资源现在可用。如果有其他线程在等待这个资源,执行V操作后会唤醒其中一个等待的线程。

2.现在有出现一个问题怎么让不同进程看到同一个count呢?
可以用共享内存 管道 让进程与进程间建立联系就可以

3.但这样又有一个新问题,count++这种操作并不是原子性的,多个线程同时修改同一数据引发数据竞争。

使用二进制信号量(或互斥锁),可以确保在任何时刻只有一个线程或进程能够访问临界区(共享资源)。当一个线程进入临界区时,它会调用 P 操作(等待),如果信号量值为 0,其他线程会被阻塞,直到该线程调用 V 操作(释放),将信号量值加 1。

信号量可以分为两种类型:

1.二进制信号量。count==1

2.计数信号量。count>1

1.semget()

可以创建nsems个信号量,一个信号量管理count个资源。

int semget(key_t key, int nsems, int semflg);
  • key: 唯一标识符。
  • nsems: 信号量的数量。
  • semflg: 权限标志(如 IPC_CREAT0666)。

2.semctl()

控制信号量集的操作,如获取、设置信号量值或删除信号量。

int semctl(int semid, int semnum, int cmd, ...);
  • semid: 信号量集标识符。
  • semnum: 信号量在集合中的索引。
  • cmd: 操作命令(如 SETVALGETVALIPC_RMID 等)。

semnum:信号量集合允许我们同时管理多个信号量,semnum 就是用来指定我们想要操作的具体信号量。在一个信号量集合中,每个信号量都有一个唯一的索引,从 0 开始编号。例如,如果一个信号量集合中有三个信号量,索引将是 0、1 和 2。

3.semop()

 执行对信号量的操作,如 P(wait)和 V(signal)。

int semop(int semid, struct sembuf *sops, size_t nsops);struct sembuf {unsigned short sem_num;  // 信号量在集合中的索引short sem_op;           // 要执行的操作(正数表示 V 操作,负数表示 P 操作)short sem_flg;          // 操作标志(如 `IPC_NOWAIT`)
};
  • semid: 信号量集标识符。
  • sops: 指向 sembuf 结构数组的指针,定义了要执行的操作。
  • nsops: 数组中操作的数量。(对nsops个信号量进行PV操作)

ipcs -s 

显示当前系统中所有的信号量信息,包括信号量的标识符、拥有者、权限和其他相关信息。

------ Semaphore Arrays --------
KEY        ID      OWNER    PERMS      NSEMS
0x12345678 12345   user     666        1

System V怎么实现IPC的

1.应用角度,看IPC属性

struct shmid_ds里面是共享内存的属性,第一个成员变量就是struct ipc_prem结构体。而消息队列 信号量同样也是以struct ipc_prem结构体开头,struct ipc_prem结构体里面就保存着key值。也就是说不同 IPC 机制可以通过相同的方式来管理和控制访问权限,不同的IPC机制都有自己的一套key值,所以共享内存 消息队列 信号量的key可能重复,但在一种IPC机制中key是唯一的。

2.从内核角度,看IPC结构

有全局的结构体ipc_ids,里面有struct ipc_id_ary* entries指向结构体ipc_id_ary。

结构体ipc_id_ary最后一个成员变量是柔性数组,也就是说它可以动态保存kern_ipc_perm指针。kern_ipc_perm结构体里面保存的是IPC不同机制共同的属性。

为什么说kern_ipc_perm结构体里面保存的是IPC不同机制共同的属性?

因为在不同PC机制的属性中第一个成员变量就是kern_ipc_perm结构体,其他成员变量都是根据不同IPC的实现机制特别增加的。

为什么不同IPC机制的属性中第一个成员变量一定是kern_ipc_perm结构体?

因为结构体ipc_id_ary中柔性数组保存的是kern_ipc_perm指针,如果共享内存 消息队列 信号量它们属性第一个成员变量是kern_ipc_perm,那么就可以指向他们的结构体属性。

这样ipc_id_ary数组就可以找到每个创建的共享内存 消息队列 信号量的属性,进而进行管理。

其实我们shmget() msgget() semget()获取的id就是它们在ipc_id_ary数组的下标。

和key值一样,id在一种IPC机制中是唯一的,但在不同IPC机制中可能相同。

也就是说一个ipc_id_ary数组中指向的全是相同IPC机制的属性。

其实共享内存是一种文件

在共享内存的属性中有struct file*的指针,它指向一个文件,文件有inode,知道它的数据块的物理地址。vm_start vm_end是共享内存在虚拟地址空间的起始地址 结束地址,把它与指向文件的内存块的物理地址建立映射关系,进程可以通过这个映射直接访问文件内容。

相关文章:

Linux 进程间通信 共享内存_消息队列_信号量

共享内存 共享内存是一种进程间通信&#xff08;IPC&#xff09;机制&#xff0c;它允许多个进程访问同一块内存区域。这种方法可以提高效率&#xff0c;因为数据不需要在进程之间复制&#xff0c;而是可以直接在共享的内存空间中读写。 使用共享内存的步骤通常包括&#xff1a…...

Mybatis自定义日志打印

一&#xff0c;目标 替换?为具体的参数值统计sql执行时间记录执行时间过长的sql&#xff0c;并输出信息到文档&#xff08;以天为单位进行存储&#xff09; 平常打印出来的sql都是sql一行&#xff0c;参数一行。如图&#xff1a; 二&#xff0c;理论 这里我们主要通过Mybatis…...

【在Linux世界中追寻伟大的One Piece】Socket编程TCP(续)

目录 1 -> V2 -Echo Server多进程版本 2 -> V3 -Echo Server多线程版本 3 -> V3-1 -多线程远程命令执行 4 -> V4 -Echo Server线程池版本 1 -> V2 -Echo Server多进程版本 通过每个请求&#xff0c;创建子进程的方式来支持多连接。 InetAddr.hpp #pragma…...

面试高频问题:C/C++编译时内存五个分区

在面试时,C/C++编译时内存五个分区是经常问到的问题,面试官通过这个问题来考察面试者对底层的理解。在平时开发时,懂编译时内存分区,也有助于自己更好管理内存。 目录 内存分区的定义 内存分区的重要性 代码区 数据区 BSS区 堆区 栈区 静态内存分配 动态内存分配…...

阅读博士论文《功率IGBT模块健康状态监测方法研究》

IGBT的失效可以分为芯片级失效和封装级失效。其中封装级失效是IGBT模块老化的主要原因&#xff0c;是多种因素共同作用的结果。在DBC的这种结构中&#xff0c;流过芯片的负载电流通过键合线传导到 DBC上层铜箔&#xff0c;再经过端子流出模块。DBC与芯片和提供机械支撑的基板之…...

Spring ApplicationContext接口

ApplicationContext接口是Spring框架中更高级的IoC容器接口&#xff0c;扩展了BeanFactory接口&#xff0c;提供了更多的企业级功能。ApplicationContext不仅具备BeanFactory的所有功能&#xff0c;还增加了事件发布、国际化、AOP、资源加载等功能。 ApplicationContext接口的…...

[perl] 数组与哈希

数组变量以 符号开始&#xff0c;元素放在括号内 简单举例如下 #!/usr/bin/perl names ("a1", "a2", "a3");print "\$names[0] $names[0]\n"; print "size: ",scalar names,"\n";$new_names shift(names); …...

电机学习-SPWM原理及其MATLAB模型

SPWM原理及其MATLAB模型 一、SPWM原理二、基于零序分量注入的SPWM三、MATLAB模型 一、SPWM原理 SPWM其实是相电压的控制方式&#xff0c;定义三相正弦相电压的表达式&#xff1a; { V a m V m sin ⁡ ω t V b m V m sin ⁡ ( ω t − 2 3 π ) V c m V m sin ⁡ ( ω t 2…...

群控系统服务端开发模式-应用开发-腾讯云上传工厂及七牛云上传工厂开发

记住业务流程图&#xff0c;要不然不清楚自己封装的是什么东西。 一、腾讯云工厂开发 切记在根目录下要安装腾讯云OSS插件&#xff0c;具体代码如下&#xff1a; composer require qcloud/cos-sdk-v5 在根目录下extend文件夹下Upload文件夹下channel文件夹中&#xff0c;我们修…...

【深度学习滑坡制图|论文解读3】基于融合CNN-Transformer网络和深度迁移学习的遥感影像滑坡制图方法

【深度学习滑坡制图|论文解读3】基于融合CNN-Transformer网络和深度迁移学习的遥感影像滑坡制图方法 【深度学习滑坡制图|论文解读3】基于融合CNN-Transformer网络和深度迁移学习的遥感影像滑坡制图方法 文章目录 【深度学习滑坡制图|论文解读3】基于融合CNN-Transformer网络和…...

《计算机原理与系统结构》学习系列——处理器(下)

系列文章目录 目录 流水线冒险数据冒险数据相关与数据冒险寄存器先读后写旁路取数使用型冒险阻塞 控制冒险分支引发的控制冒险假设分支不发生动态分支预测双预测位动态分支预测缩短分支延迟带冒险控制的单周期流水线图 异常MIPS中的异常MIPS中的异常处理另一种异常处理机制非精…...

JDK新特性(8-21)数据类型-直接内存

目录 Jdk 新特性 JDK 8 特性 默认方法实现作用:可以使接口更加灵活&#xff0c;不破坏现有实现的情况下添加新的方法。 函数式接口 StreamAPI JDK 9 特性 JDK 10 特性 JDK 11 特性 JDK 14 特性 JDK 17 特性 JDK 21 特性 数据类型 基本数据类型和引用数据类型的区别…...

003-Kotlin界面开发之声明式编程范式

概念本源 在界面程序开发中&#xff0c;有两个非常典型的编程范式&#xff1a;命令式编程和声明式编程。命令式编程是指通过编写一系列命令来描述程序的运行逻辑&#xff0c;而声明式编程则是通过编写一系列声明来描述程序的状态。在命令式编程中&#xff0c;程序员需要关心程…...

QT pro项目工程的条件编译

QT pro项目工程的条件编译 前言 项目场景&#xff1a;项目中用到同一型号两个相机&#xff0c;同时导入两个版本有冲突&#xff0c;编译不通过&#xff0c; 故从编译就区分相机导入调用&#xff0c;使用宏区分 一、定义宏 在pro文件中定义宏&#xff1a; DEFINES USE_Cam…...

深度学习之经典网络-AlexNet详解

AlexNet 是一种经典的卷积神经网络&#xff08;CNN&#xff09;架构&#xff0c;在 2012 年的 ImageNet 大规模视觉识别挑战赛&#xff08;ILSVRC&#xff09;中表现优异&#xff0c;将 CNN 引入深度学习的新时代。AlexNet 的设计在多方面改进了卷积神经网络的架构&#xff0c;…...

部署Prometheus、Grafana、Zipkin、Kiali监控度量Istio

1. 模块简介 Prometheus 是一个开源的监控系统和时间序列数据库。Istio 使用 Prometheus 来记录指标&#xff0c;跟踪 Istio 和网格中的应用程序的健康状况。Grafana 是一个用于分析和监控的开放平台。Grafana 可以连接到各种数据源&#xff0c;并使用图形、表格、热图等将数据…...

结合 Spring Boot Native 和 Spring Boot 构建高性能服务器架构

随着云计算和微服务架构的普及&#xff0c;开发者们不断寻求提高应用性能和用户体验的解决方案。Spring Boot Native 的出现&#xff0c;利用 GraalVM 的原生映像特性&#xff0c;使得 Java 应用的启动速度和资源占用得到了显著改善。本文将深入探讨如何将前端应用使用 Spring …...

ArcGIS影像调色(三原色)三原色调整

本期主要介绍ArcGIS影像调色&#xff08;三原色&#xff09; ArcGIS影像调色&#xff08;三原色&#xff09;&#xff0c;对比度、亮度、gamma。红绿蓝三原色调整。 视频学习 ArcGIS影像调色&#xff08;三原色&#xff09;...

SQLite从入门到精通面试题及参考答案

目录 SQLite 是什么? SQLite 的优点有哪些? 轻量级与易于部署 零配置和低维护成本 良好的兼容性和跨平台性 高性能和可靠性 SQLite 的局限性有哪些? 并发处理能力有限 缺乏用户管理和权限控制功能 有限的扩展性 有限的网络支持 SQLite 和其他数据库系统(如 MyS…...

【C/C++】字符/字符串函数(0)(补充)——由ctype.h提供

零.导言 除了字符分类函数&#xff0c;字符转换函数也是一类字符/字符串函数。 C语言提供了两种字符转换函数&#xff0c;分别是 toupper &#xff0c; tolower。 一.什么是字符转换函数&#xff1f; 顾名思义&#xff0c;即转换字符的函数&#xff0c;如大写字母转小写字母&am…...

k8s从入门到放弃之Ingress七层负载

k8s从入门到放弃之Ingress七层负载 在Kubernetes&#xff08;简称K8s&#xff09;中&#xff0c;Ingress是一个API对象&#xff0c;它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress&#xff0c;你可…...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

1688商品列表API与其他数据源的对接思路

将1688商品列表API与其他数据源对接时&#xff0c;需结合业务场景设计数据流转链路&#xff0c;重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点&#xff1a; 一、核心对接场景与目标 商品数据同步 场景&#xff1a;将1688商品信息…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

C# SqlSugar:依赖注入与仓储模式实践

C# SqlSugar&#xff1a;依赖注入与仓储模式实践 在 C# 的应用开发中&#xff0c;数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护&#xff0c;许多开发者会选择成熟的 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;SqlSugar 就是其中备受…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容

目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法&#xff0c;当前调用一个医疗行业的AI识别算法后返回…...

深度学习习题2

1.如果增加神经网络的宽度&#xff0c;精确度会增加到一个特定阈值后&#xff0c;便开始降低。造成这一现象的可能原因是什么&#xff1f; A、即使增加卷积核的数量&#xff0c;只有少部分的核会被用作预测 B、当卷积核数量增加时&#xff0c;神经网络的预测能力会降低 C、当卷…...

Python 包管理器 uv 介绍

Python 包管理器 uv 全面介绍 uv 是由 Astral&#xff08;热门工具 Ruff 的开发者&#xff09;推出的下一代高性能 Python 包管理器和构建工具&#xff0c;用 Rust 编写。它旨在解决传统工具&#xff08;如 pip、virtualenv、pip-tools&#xff09;的性能瓶颈&#xff0c;同时…...