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

[操作系统] 进程间通信:system V共享内存

文章目录

  • 内存共享示意图
  • 共享内存的管理代码
  • shmget 函数
    • 原理
      • 先创建共享内存
      • 如何保证两个不同的进程拿到的是一块共享内存
  • `shmctl` 函数
    • `IPC_RMID`参数
  • `shmat`函数
    • 无同步机制
      • 同步机制是什么
      • 模拟演示非同步效果
      • 如何提供保护机制
  • `shmdt`
  • 再谈描述共享内存的数据结构
    • `struct shmid_ds`
    • `struct ipc_perm`

System V 是UNIX操作系统的一个版本,它定义了一系列的标准,包括进程间通信(IPC)的标准。Linux操作系统的内核实现了 <font style="color:rgb(6, 6, 7);">System V</font> 定义的IPC标准,并为此专门设计了一个模块来处理进程间的通信。进程间通信(IPC)的核心目的是允许不同的进程能够访问和操作同一份资源。这样,进程之间就可以通过共享资源来交换信息。不同的IPC机制可能在接口和原理上有相似之处,使得开发者可以更容易地理解和使用这些机制。


共享内存(Shared Memory)是进程间通信(IPC)的一种机制,它允许两个或多个进程访问同一块内存空间。共享内存是<font style="color:rgb(6, 6, 7);">System V</font> IPC标准的一部分,它提供了一种高效的进程间数据交换方式。

共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进⼊内核的系统调用来传递彼此的数据。

内存共享示意图

这是进程地址空间示意图,其中一部分为共享区,会存储共享内存、内存映射、共享库等。

这个区域如何使用呢?对于共享内存来说:

进程的共享区中存储的地址实际上是虚拟地址。当需要使用共享内存时,整个过程可以更清晰地描述如下:

  1. 数据加载到物理内存:首先,系统会将需要共享的数据加载到一块物理内存区域中,这块物理内存将成为多个进程共享的基础。
  2. 虚拟地址的建立:每个需要访问共享数据的进程会在自己的虚拟地址空间中开辟一个区域(通常称为共享区),并为该区域分配一个虚拟地址。
  3. 页表映射:通过进程的页表,这个虚拟地址会被映射到物理内存中那块存放共享数据的具体地址上。
  4. 实现数据共享:由于不同进程的虚拟地址通过各自的页表指向同一块物理内存区域,多个进程就能通过自己的虚拟地址访问相同的共享数据。

这种机制使得进程能够高效地共享内存中的数据,避免了不必要的数据复制,从而提升了系统的性能和资源利用效率。

共享内存通常有一个引用计数(Reference Count),记录当前有多少进程正在使用这块共享内存。共享内存会在引用计数为0时释放:

  • 只有当所有进程都解除对共享内存的映射(即引用计数降为0)时,共享内存才会被标记为可销毁。
  • 在Linux中,共享内存段由shmget创建时分配一个标识符(shmid),并通过shmctl系统调用管理。如果引用计数为0,系统会根据共享内存的设置决定是否立即销毁。

同一时刻可能会存在多组进程,都在使用不同的共享内存来进行通信。也就是说在操作系统中会有多个共享内存同时存在,那么按照以往的经验以及操作系统的习惯,这些共享内存是否需要管理呢?

和之前一样,先描述,再组织!

前面所提的引用计数当然也就是在管理共享内存的数据结构中进行维护。

共享内存的管理代码

struct shmid_ds {struct ipc_perm shm_perm;    /* 操作权限 */int shm_segsz;              /* 共享内存段的大小(字节) */__kernel_time_t shm_atime;  /* 最后一次附加时间 */__kernel_time_t shm_dtime;  /* 最后一次分离时间 */__kernel_time_t shm_ctime;  /* 最后一次更改时间 */__kernel_ipc_pid_t shm_cpid;/* 创建者的进程 ID */__kernel_ipc_pid_t shm_lpid;/* 最后操作者的进程 ID */unsigned short shm_nattch;  /* 当前附加的进程数量 */unsigned short shm_unused;  /* 兼容性字段 */void *shm_unused2;          /* 兼容性字段(DIPC 使用) */void *shm_unused3;          /* 未使用字段 */
};

这个结构包含 11 个字段,每个字段都有特定的用途。让我们逐一讲解。


字段解析

  1. struct ipc_perm shm_perm - 操作权限
  • 作用:定义共享内存段的访问权限和所有权。
  • 详情:shm_perm 是一个嵌套的 struct ipc_perm 结构,包含创建者的用户 ID (uid)、组 ID (gid) 和权限模式(类似于文件权限,例如 0666)。它确保只有授权的进程可以访问或修改共享内存。
  • 这是 System V IPC 的通用权限机制,适用于共享内存、消息队列和信号量。
  1. int shm_segsz - 共享内存段大小
  • 作用:记录共享内存段的大小(单位:字节)。
  • 详情:在调用 shmget 创建共享内存时指定这个值,进程可以通过它知道可用内存的大小。
  1. __kernel_time_t shm_atime - 最后附加时间
  • 作用:记录最近一次进程附加(attach)到共享内存的时间。
  • 详情:当进程调用 shmat 附加共享内存时,这个字段更新为当前时间戳。
  1. __kernel_time_t shm_dtime - 最后分离时间
  • 作用:记录最近一次进程分离(detach)共享内存的时间。
  • 详情:调用 shmdt 时更新,反映共享内存的活跃状态。
  1. __kernel_time_t shm_ctime - 最后更改时间
  • 作用:记录共享内存属性的最后修改时间。
  • 详情:通过 shmctl 修改权限或其他属性时更新。
  1. __kernel_ipc_pid_t shm_cpid - 创建者 PID
  • 作用:保存创建共享内存的进程 ID。
  • 详情:由 shmget 调用时记录,用于追踪共享内存的所有权。
  1. __kernel_ipc_pid_t shm_lpid - 最后操作者 PID
  • 作用:记录最后操作共享内存的进程 ID。
  • 详情:包括附加(shmat)、分离(shmdt)或修改(shmctl)等操作。
  1. unsigned short shm_nattch - 当前附加进程数量
  • 作用:统计当前附加到共享内存的进程数。
  • 详情:这是引用计数的关键字段,进程附加时加 1,分离时减 1。当值为 0 时,共享内存可能被销毁(取决于标志)。
  1. unsigned short shm_unused - 兼容性字段
  • 作用:保留字段,未在现代 Linux 中使用。
  • 详情:早期可能有用途,现仅为兼容性保留。
  1. void *shm_unused2 - 兼容性字段(DIPC 使用)
  • 作用:同样是保留字段,可能在分布式 IPC (DIPC) 中使用。
  • 详情:标准 Linux 内核未使用。
  1. void *shm_unused3 - 未使用字段
  • 作用:完全未使用,可能为未来扩展保留。
  • 详情:对齐或占位用途。

shmget 函数

功能:创建共享内存段
原型

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

参数

  • key:不同进程shm来进行通信,作为共享内存段的唯一标识(键值),通过用户层来传入,但是只是对于内核来作为唯一标识,用户层使用该函数的返回值int作为唯一表示。
  • size:共享内存大小(单位:字节)。
  • shmflg:权限标志组合,常见取值:
    • IPC_CREAT:若共享内存不存在则创建,否则获取已存在的共享内存。
    • IPC_CREAT | IPC_EXCL:若共享内存不存在则创建,若已存在则报错(确保唯一性),也就是说只要成功返回就一定是一个全新的共享内存。

返回值

  • 成功:返回共享内存段的标识符(非负整数)。
  • 失败:返回 -1

**size**的细节处理:

在内核中,共享内存在创建的时候它的大小必须是以4KB(4096)的整数倍为单位。

如果我们给size传入4097的话,它会向上取整,也就是开辟4096*2,但是在实际上显示的时候仍然显示4097,因为你指定的使用空间是4097,但是底层仍然是按照单位开辟空间。

原理

例如现在需要两个进程共享一块内存。

先创建共享内存

对于使用shmget函数时要传入的key值,使用ftok函数来得到要传入的key

#include <sys/types.h>
#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);

功能:

<font style="color:black;">ftok</font> 函数通过一个现有的文件路径名(<font style="color:black;">pathname</font>)和一个项目标识符(<font style="color:black;">proj_id</font>)生成一个唯一的 <font style="color:black;">key_t</font> 值。这个键值可以用作 IPC 对象(如共享内存)的标识符,确保不同进程能够访问相同的 IPC 资源。


参数:

  1. <font style="color:black;">pathname</font>
    • 类型:<font style="color:black;">const char *</font>
    • 描述:指向一个现有文件路径的字符串,通常是一个可访问的文件的绝对路径。
    • 要求:文件必须存在且当前进程有权限访问,否则函数会失败。
  2. <font style="color:black;">proj_id</font>
    • 类型:<font style="color:black;">int</font>
    • 描述:一个非零的项目标识符,通常取值范围为 1 到 255(低 8 位有效)。
    • 作用:与 <font style="color:black;">pathname</font> 结合,增加键值的唯一性。

返回值:

  • 成功:返回一个 key_t 类型的非负整数,作为 IPC 对象的键值
  • 失败:返回 -1,并设置 errno 以指示错误原因(例如,文件不存在或无权限)。

工作原理:

<font style="color:black;">ftok</font> 函数通过以下步骤生成键值:

  1. 获取 <font style="color:black;">pathname</font> 对应的文件信息(如索引节点号<font style="color:black;"> </font><font style="color:black;">inode</font> 和设备号 <font style="color:black;">dev</font>)。
  2. 将文件的 <font style="color:black;">inode</font> 号、设备号与 <font style="color:black;">proj_id</font> 的低 8 位组合起来,生成一个唯一的 <font style="color:black;">key_t</font> 值。
  3. 返回生成的键值。

生成的键值并不是完全随机的,而是基于文件系统信息和 <font style="color:black;">proj_id</font> 的确定性结果。因此:

  • 如果两个进程使用相同的 <font style="color:black;">pathname</font><font style="color:black;">proj_id</font>,它们会得到相同的 <font style="color:black;">key_t</font><font style="color:black;"> </font>值。
  • 如果 <font style="color:black;">pathname</font><font style="color:black;">proj_id</font> 不同,通常会生成不同的键值。

如何保证两个不同的进程拿到的是一块共享内存

当通过ftok得到key值并传入shmget后,即可得到一块共享内存,shmget函数的返回值就是用户层面上对于共享内存的唯一表示符。key值是对于操作系统而言的唯一标识符。

当想让两个不同的进程拿到同一块共享内存,比如有A进程和B进程,它们现在想要共享shmget创建的这一块共享内存。

A进程把key传给操作系统内核,然后操作系统使用这个key设置为开辟的共享内存(物理内存)的唯一值,之后B进程链接该共享内存时会辨别传入

<font style="color:black;">shmctl</font> 函数

<font style="color:black;">shmctl</font> 是 System V IPC 中用于控制共享内存的核心函数。它提供了对共享内存段的多种操作,包括获取状态、修改属性和删除内存段等。

函数定义

  • 头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
  • 原型:
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

功能

<font style="color:black;">shmctl</font> 用于控制共享内存段的状态和行为。通过不同的命令(cmd),它可以:

  • 获取共享内存的当前状态。
  • 修改共享内存的属性(如权限)。
  • 标记共享内存段为待删除。

参数

  1. <font style="color:black;">shmid</font>
    • 类型:<font style="color:black;">int</font>
    • 描述:共享内存的标识符,由 <font style="color:black;">shmget</font><font style="color:black;"> </font>函数返回。
    • 作用:指定要操作的共享内存段。
  2. <font style="color:black;">cmd</font>
    • 类型:<font style="color:black;">int</font>
    • 描述:指定要执行的动作,可取以下三个值:
      • <font style="color:black;">IPC_STAT</font>:获取共享内存的状态,将信息存储到 <font style="color:black;">buf</font><font style="color:black;"> </font>指向的 <font style="color:black;">struct shmid_ds</font> 结构体中。
      • <font style="color:black;">IPC_SET</font>:设置共享内存的属性,从 <font style="color:black;">buf</font><font style="color:black;"> </font>指向的 <font style="color:black;">struct shmid_ds</font> 中读取新值。
      • <font style="color:black;">IPC_RMID</font>:标记共享内存段为待删除,当所有进程脱离后销毁。
    • 作用:决定 shmctl 的具体行为。
  3. <font style="color:black;">buf</font>
    • 类型:<font style="color:black;">struct shmid_ds *</font>
    • 描述:指向一个 <font style="color:black;">struct shmid_ds</font> 结构体的指针,用于保存或设置共享内存的状态和访问权限。
    • 细节:
      • 对于 <font style="color:black;">IPC_STAT</font><font style="color:black;">buf</font><font style="color:black;"> </font>被填充为共享内存的当前状态。
      • 对于 <font style="color:black;">IPC_SET</font><font style="color:black;">buf</font><font style="color:black;"> </font>提供要设置的新属性值。
      • 对于 <font style="color:black;">IPC_RMID</font>,此参数通常设为 <font style="color:black;">NULL</font>(不使用)。


返回值

  • 成功:返回 0。
  • 失败:返回 -1,并设置 errno 以指示错误原因(如无效的 shmid 或权限不足)。

<font style="color:black;">IPC_RMID</font>参数

当我们在程序中使用shmget后随着进程退出后,会发现开辟的共享内存并没有释放:

const char *path = ".";
key_t key = ftok(path, 'a');  // 使用单字节 proj_id(如 'a')
if (key == -1) 
{std::cerr << "ftok failed: " << strerror(errno) << std::endl;return 1;
}// 创建共享内存(权限 0600:用户可读可写)
int shmid = shmget(key, 1000, IPC_CREAT | IPC_EXCL | 0600);
if (shmid == -1) 
{std::cerr << "shmget failed: " << strerror(errno) << std::endl;return 1;
}

使用ipcs -m命令查询当前共享内存段信息:

得到结论:共享内存资源,生命周期跟随内核!

也就是说当操作系统重启后该共享内存就会释放。

但是我们可以通过其他方法进行释放共享内存:

  • bashipcs -m进行查询当前共享内存,ipcrm shmid来删除制定shmid的共享内存。
    • 为什么使用的是shmid而不是key?因为key是给内核用来区分共享内存唯一性的,所以使用shmid进行管理共享内存。
  • 程序中使用对应函数进行释放:int shmctl(int shmid, int cmd, struct shmid_ds *buf);cmd使用<font style="color:black;">IPC_RMID</font>
#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cerrno>
#include <cstring>
#include <unistd.h>int main() {const char *path = ".";key_t key = ftok(path, 'a');  // 使用单字节 proj_id(如 'a')if (key == -1) {std::cerr << "ftok failed: " << strerror(errno) << std::endl;return 1;}// 创建共享内存(权限 0600:用户可读可写)int shmid = shmget(key, 1000, IPC_CREAT | IPC_EXCL | 0600);if (shmid == -1) {std::cerr << "shmget failed: " << strerror(errno) << std::endl;return 1;}sleep(10);// 程序退出前删除共享内存if (shmctl(shmid, IPC_RMID, NULL) == -1) {std::cerr << "shmctl failed: " << strerror(errno) << std::endl;return 1;}return 0;
}

程序运行中(sleep阻挡释放共享区):

shmctl(shmid, IPC_RMID, NULL)释放shmid的共享内存:

shmat函数

shmat用于将共享内存挂接到进程的虚拟地址空间,sham-at,at对应的就是attach这个单词。

为什么需要 shmat:因为共享内存只是“存在”还不够,进程需要一个具体的虚拟地址来访问它,而 shmat 完成了这个映射过程,<font style="color:black;">shmget</font> 是“造房子”,<font style="color:black;">shmat</font><font style="color:black;"></font>是“开门入住”。

NAMEshmat, shmdt - System V shared memory operationsSYNOPSIS#include <sys/types.h>#include <sys/shm.h>void *shmat(int shmid, const void *shmaddr, int shmflg);
  • **shmid****:**共享内存的标识符,也就是shmget的返回值,不是key,key是给系统使用的。
  • **shmaddr****:**虚拟地址,使用固定的虚拟地址进行挂接,默认时填入NULL
  • **shmflg****:**为挂接在虚拟内存设置权限,两个可能取值是SHM_RNDSHM_RDONLY,使用默认设置的话传入0。
  • 返回值:成功返回一个指针,指向共享内存的第一个节(section),也就是指向挂接的那个共享内存区域在当前进程虚拟地址空间的起始位置失败返回-1。

如何理解返回值,我们得到这个起始的虚拟地址有什么用?

  • 在学习C语言时,malloc函数就是用来在堆上开辟空间,然后返回一个指针,这个指针也是指向开辟空间在虚拟地址空间中的的起始位置,只不过shmat是在共享区进行挂接,是用户可访问修改的(除非指定权限),而堆栈是不可写的。

无同步机制

同步机制是什么

同步机制(Synchronization Mechanism)是多进程或多线程编程中用来协调多个执行单元(进程或线程)访问共享资源的一种方法。它的核心目标是避免数据竞争(data race)和不一致性(inconsistency),确保共享资源(如共享内存、文件、变量等)在并发访问时能够保持正确性和一致性。

为什么需要同步机制?

  • 数据竞争:多个进程同时写入共享内存,可能导致数据被覆盖或部分写入。
  • 例如:server 正在写入 “Hello”,刚写到 “Hel” 时,client 读取,可能只读到 “Hel”。
  • 不一致性:读写顺序不可控,可能导致逻辑错误。
  • 例如:client 读取数据时,server 还没写完,读到的可能是未初始化的垃圾数据。
  • 死锁或竞争条件:如果多个进程互相等待对方完成,可能会陷入死锁。

模拟演示非同步效果

使用封装的shm类进行模拟:

写:

FileOper writerfile(PATH, FILENAME);
writerfile.OpenForWrite();Shm shm(pathname, projid, USER);
char *mem = (char *)shm.VirtualAddr();
// 我们读写共享内存,有没有使用系统调用??没有!!
int index = 0;
for (char c = 'A'; c <= 'B'; c++, index += 2)
{// 才是向共享内存写入sleep(1);mem[index] = c;mem[index + 1] = c;sleep(1);mem[index+2] = 0;writerfile.Wakeup();
}

读:

Shm shm(pathname, projid, CREATER);
// sleep(5);
shm.Attr();NamedFifo fifo(PATH, FILENAME);// 文件操作了
FileOper readerfile(PATH, FILENAME);
readerfile.OpenForRead();char *mem = (char *)shm.VirtualAddr();
// 我们读写共享内存,有没有使用系统调用??也没有!!
while (true)
{if (readerfile.Wait()){printf("%s\n", mem); // 每次读取共享内存区上的全部内容}elsebreak;
}

可以发现在往共享内存写入和读取的时候直接使用C语言提供的函数进行操作,而不是系统调用。并且还没有写入完整的内容就已经被一直读取了,这就是因为共享内存没有所谓的“同步机制”,没有内核文件缓冲区那样的中间层。但是这样的方式使得共享内存是进程间通信最快的方式

  • 在映射之后,读写可以直接被对方看到,直接在内存上读写。
  • 不需要使用系统调用获取或者写入内容。

因为共享区属于用户空间,可以让用户直接使用!

如何提供保护机制

共享内存本身不提供访问控制或保护机制。所有映射了这块内存的进程都可以自由读写,没有限制,这就是缺点。因此程序员需要自己实现保护机制。

如使用管道机制的阻塞来手动同步:

<font style="color:black;">server</font>端运行时,阻塞等待。

以此方法来作为有控制性的读取和输入的保证。

shmdt

shmdt函数 功能:将共享内存段与当前进程脱离 
原型 int shmdt(const void *shmaddr); 
参数 shmaddr: 由shmat所返回的指针,指向共享区内虚拟地址的指针返回值:成功返回0;失败返回-1 注意:将共享内存段与当前进程脱离,不等于删除共享内存段

演示代码:

// _start_mem 为shmat返回值,即共享区对应虚拟地址起始位置
int n = shmdt(_start_mem);
if (n == 0)
{printf("detach success\n");
}

再谈描述共享内存的数据结构

struct shmid_ds

struct shmid_ds即为描述共享内存的数据结构,每一个由shmget获取的共享内存都由struct shmid_ds组织管理:

/* 描述共享内存段的数据结构 */
struct shmid_ds{struct ipc_perm shm_perm;   /* 操作权限结构 */size_t shm_segsz;           /* 段的大小(以字节为单位) */__time_t shm_atime;         /* 最后一次 shmat() 的时间 */
#ifndef __x86_64__unsigned long int __unused1; /* 未使用的字段1(仅在非x86_64架构中) */
#endif__time_t shm_dtime;         /* 最后一次 shmdt() 的时间 */
#ifndef __x86_64__unsigned long int __unused2; /* 未使用的字段2(仅在非x86_64架构中) */
#endif__time_t shm_ctime;         /* 最后一次通过 shmctl() 更改的时间 */
#ifndef __x86_64__unsigned long int __unused3; /* 未使用的字段3(仅在非x86_64架构中) */
#endif__pid_t shm_cpid;           /* 创建者的进程ID */__pid_t shm_lpid;           /* 最后一次共享内存操作的进程ID */shmatt_t shm_nattch;        /* 当前附着的数量 */};

struct ipc_perm

**struct ipc_perm**为操作权限结构 :

/* 用于向 IPC 操作传递权限信息的数据结构 */
struct ipc_perm{__key_t __key;              /* 键值 */__uid_t uid;                /* 所有者的用户ID */__gid_t gid;                /* 所有者的组ID */__uid_t cuid;               /* 创建者的用户ID */__gid_t cgid;               /* 创建者的组ID */unsigned short int mode;    /* 读/写权限 */unsigned short int __pad1;  /* 填充字段1 */unsigned short int __seq;   /* 序列号 */unsigned short int __pad2;  /* 填充字段2 */__syscall_ulong_t __unused1; /* 未使用的字段1 */__syscall_ulong_t __unused2; /* 未使用的字段2 */};

通过struct ipc_perm即可得到_key值等信息。

实力代码:

struct shmid_ds ds;
int n = shmctl(_shmid, IPC_STAT, &ds); // ds:输出型参数
printf("shm_segsz: %ld\n", ds.shm_segsz);
printf("key: 0x%x\n", ds.shm_perm.__key);

得以验证_key即为使用shmget时传入的key

相关文章:

[操作系统] 进程间通信:system V共享内存

文章目录 内存共享示意图共享内存的管理代码shmget 函数原理先创建共享内存如何保证两个不同的进程拿到的是一块共享内存 shmctl 函数IPC_RMID参数 shmat函数无同步机制同步机制是什么模拟演示非同步效果如何提供保护机制 shmdt再谈描述共享内存的数据结构struct shmid_dsstruc…...

科技快讯 | 阿里云百炼MCP服务上线;英伟达官宣:CUDA 工具链将全面原生支持 Python

李飞飞团队最新AI报告&#xff1a;中美模型性能差距近乎持平 4月8日&#xff0c;斯坦福大学以人为本人工智能研究所发布《2025年人工智能指数报告》。报告显示&#xff0c;2023年AI性能显著提升&#xff0c;AI应用加速&#xff0c;投资增长&#xff0c;中美AI模型差距缩小。报告…...

踩雷,前端一直卡在获取token中

问题&#xff1a;一直卡在var token SecureStorage.Default.GetAsync("auth_token").Result; public VideoService(){_httpClient new HttpClient();var token SecureStorage.Default.GetAsync("auth_token");} 这是一个典型的同步等待异步操作导致的死…...

MATLAB双目标定

前言&#xff1a; 现在有许多双目摄像头在出厂时以及标定好&#xff0c;用户拿到手后可以直接使用&#xff0c;但也有些双目摄像头在出厂时并没有标定。因而这个时候就需要自己进行标定。本文主要介绍基于matlab工具箱的自动标定方式来对双目相机进行标定。 1、MATLAB工具箱标…...

4.10学习总结

完成两道算法题&#xff08;感觉对贪心的思路很难去想到如何解&#xff09; 完成stream流的学习&#xff0c;开始学习方法引用...

QML自定义组件

自定义组件是 QML 开发中的核心概念&#xff0c;它允许您创建可重用的 UI 元素和逻辑单元。以下是创建和使用自定义组件的完整方法。 1. 基本自定义组件创建 创建单独组件文件 (推荐方式) qml // MyButton.qml&#xff08;单独一个qml文件&#xff09;import QtQuick 2.15R…...

多模态大语言模型arxiv论文略读(十)

Towards End-to-End Embodied Decision Making via Multi-modal Large Language Model: Explorations with GPT4-Vision and Beyond ➡️ 论文标题&#xff1a;Towards End-to-End Embodied Decision Making via Multi-modal Large Language Model: Explorations with GPT4-Vi…...

关于 Spring Boot + Vue 前后端开发的打包、测试、监控、预先编译和容器部署 的详细说明,涵盖从开发到生产部署的全流程

以下是关于 Spring Boot Vue 前后端开发的打包、测试、监控、预先编译和容器部署 的详细说明&#xff0c;涵盖从开发到生产部署的全流程&#xff1a; 1. 打包 1.1 后端&#xff08;Spring Boot&#xff09; 打包方式 使用 Maven 或 Gradle 打包成可执行的 JAR/WAR 文件&…...

【Raqote】 1.1 路径填充ShaderMaskBlitter 结构体(blitter.rs)

ShaderMaskBlitter 结构体实现了 Blitter trait&#xff0c;用于带遮罩的着色器渲染。 结构体定义 pub struct ShaderMaskBlitter<a> {pub x: i32, // 目标区域的起始x坐标pub y: i32, // 目标区域的起始y坐标pub shader: &a dyn Shader, //…...

如何用 esProc 实现 Oracle 和 MySQL 的混合运算

逻辑数仓可以实现多源混算&#xff0c;但需要配置视图、预处理数据&#xff0c;结构太沉重。duckdb 是轻量级的方案&#xff0c;但没有内置 Oracle 的 connector&#xff0c;自己开发难度又太高。同为轻量级方案&#xff0c;esProc 支持 JDBC 公共接口&#xff0c;可以实现任何…...

zabbix和prometheus选择那个监控呢

文章目录 Zabbix 介绍概述架构组成特点适用场景 Prometheus 介绍概述架构组成特点适用场景 Zabbix vs Prometheus 对比架构与组件Zabbix 架构Prometheus 架构 监控要点与最佳实践告警与可视化ZabbixPrometheus Alertmanager Grafana 伸缩与高可用ZabbixPrometheus 运维成本与…...

SQL 查询中使用 IN 导致性能问题的解决方法

当 SQL 查询中使用 IN 子句导致查询长时间运行或挂起时&#xff0c;通常是由于以下几个原因造成的&#xff1a; 常见原因 IN 列表中的值过多 - 当 IN 子句包含大量值时&#xff08;如数千或更多&#xff09;&#xff0c;数据库需要处理大量比较操作 缺乏合适的索引 - 被查询的…...

UML-饮料自助销售系统(无法找零)序列图

一、题目&#xff1a; 在饮料自动销售系统中&#xff0c;顾客选择想要的饮料。系统提示需要投入的金额&#xff0c;顾客从机器的前端钱币口投入钱币&#xff0c;钱币到达钱币记录仪&#xff0c;记录仪更新自己的选择。正常时记录仪通知分配器分发饮料到机器前端&#xff0c;但可…...

Go语言中的runtime包是用来做什么的?

在Go语言中&#xff0c;runtime包提供了与Go运行时系统的交互接口。以下是runtime包的主要功能和用途&#xff1a; 1. 运行时信息 runtime包可以获取关于Go程序运行时的信息&#xff0c;包括&#xff1a; 内存使用情况&#xff1a;可以查看内存分配和使用的统计信息&#xf…...

【Linux】用C++实现UDP通信:详解socket编程流程

文章目录 协议&#xff08;Protocol&#xff09;协议的核心要素常见协议分类 UDP协议&#xff08;用户数据报协议&#xff09;1. 基本定义2. 核心特性 UDP协议实现通信服务器端Comm.hppInetAddr.hppUdpServer.hppUdpServer.cc 客户端 总结 协议&#xff08;Protocol&#xff09…...

代码随想录-06-二叉树-02.二叉树的递归遍历

二叉树的递归遍历 递归思路 确定递归函数的参数parameter和返回值确定终止条件确定单层递归逻辑 具体代码 CPP 前序遍历 vector<int> res; void traversal(TreeNode *root){if(!root)return;res.push_back(root->val);traversal(root->left);traversal(root-…...

一文详解ffmpeg环境搭建:Ubuntu系统ffmpeg配置nvidia硬件加速

在Ubuntu系统下安装FFmpeg有多种方式,其中最常用的是通过apt-get命令和源码编译安装。本文将分别介绍这两种方式,并提供安装过程。 一、apt-get安装 使用apt-get命令安装FFmpeg是最简单快捷的方式,只需要在终端中输入以下命令即可: # 更新软件包列表 sudo apt-get updat…...

(四)深入理解AVFoundation-播放:高度自定义视频播放器 UI

引言 在之前的博客中&#xff0c;我们已经介绍了如何实现一个简单的播放器&#xff0c;并通过监听资源和播放器的属性来提升播放体验。因此本篇博客将带你进一步自定义播放器 UI。通过构建自己的播放控制界面&#xff08;如播放/暂停按钮、进度条、全屏切换等&#xff09;&…...

sqli-labs靶场 less6

文章目录 sqli-labs靶场less 6 报错注入 sqli-labs靶场 每道题都从以下模板讲解&#xff0c;并且每个步骤都有图片&#xff0c;清晰明了&#xff0c;便于复盘。 sql注入的基本步骤 注入点注入类型 字符型&#xff1a;判断闭合方式 &#xff08;‘、"、’、“”&#xf…...

数据库架构全解析:MyCat、MHA、ProxySQL 的原理、功能与实例

前言 &#xff1a; 在分布式数据库架构中&#xff0c;分库分表、高可用性&#xff08;HA&#xff09;和查询优化是核心需求。本文将深入解析三款主流工具&#xff1a;MyCat&#xff08;分布式数据库中间件&#xff09;、MHA&#xff08;MySQL高可用方案&#xff09;、ProxySQL…...

【hadoop】Hive数据仓库安装部署

一、MySQL的安装与配置 换源&#xff1a; 最下面附加部分 1、在master上直接使用yum命令在线安装MySQL数据库&#xff1a; sudo yum install mysql-server 途中会询问是否继续&#xff0c;输入Y并按回车。 2、启动MySQL服务&#xff1a; sudo service mysqld start 3、设…...

Unity Addressables资源生命周期自动化监控技术详解

一、Addressables资源生命周期管理痛点 1. 常见资源泄漏场景 泄漏类型典型表现检测难度隐式引用泄漏脚本持有AssetReference未释放高异步操作未处理AsyncOperationHandle未释放中循环依赖泄漏资源相互引用无法释放极高事件订阅泄漏未取消事件监听导致对象保留高 2. 传统管理…...

Linux网络编程——深入理解TCP的可靠性、滑动窗口、流量控制、拥塞控制

目录 一、前言 二、流量控制 三、TCP的滑动窗口 1、原理 2、机制 3、数据重发 Ⅰ、只是确认应答包(ACK)丢了 Ⅱ、发送数据包丢失 4、缓冲区结构 四、TCP的拥塞控制 1、慢启动 2、拥塞避免 3、快速重传 4、快速恢复 五、延迟应答 六、捎带应答 七、再谈TCP的面…...

Manifold-IJ 2022.1.21 版本解析:IntelliJ IDEA 的 Java 增强插件指南

Manifold-IJ-2022.1.21 可能是 IntelliJ IDEA 的一个插件或相关版本&#xff0c;特别是与 Manifold 这个增强 Java 开发体验的框架相关的组件。 很多时候没有网络环境&#xff0c;而又需要这个插件。 Manifold-IJ 2022.1.21下载&#xff1a;https://pan.quark.cn/s/ad907344c…...

linux内核

一 初识linux内核 1.1操作系统和内核简介 操作系统的精确定义并没有一个统一的标准&#xff0c;这里我认为操作系统是指整个系统负责完成最基本功能和系统管理的那些部分 这些部分包括内核&#xff0c;设备驱动程序&#xff0c;启动引导程序&#xff0c;基本的文件管理工具和…...

基于CNN-LSTM-GRU的深度Q网络(Deep Q-Network,DQN)求解移动机器人路径规划,MATLAB代码

一、深度Q网络&#xff08;Deep Q-Network&#xff0c;DQN&#xff09;介绍 1、背景与动机 深度Q网络&#xff08;DQN&#xff09;是深度强化学习领域的里程碑算法&#xff0c;由DeepMind于2013年提出。它首次在 Atari 2600 游戏上实现了超越人类的表现&#xff0c;解决了传统…...

C++23新特性:显式对象形参与显式对象成员函数

文章目录 一、背景与动机二、语法与基本使用三、优势与应用场景&#xff08;一&#xff09;简化代码&#xff08;二&#xff09;提升模板编程灵活性&#xff08;三&#xff09;与Lambda表达式结合 四、限制与注意事项五、总结 C23标准引入了一项重要的语言特性——显式对象形参…...

leetcode_242. 有效的字母异位词_java

242. 有效的字母异位词https://leetcode.cn/problems/valid-anagram/ 1、题目 给定两个字符串 s 和 t &#xff0c;编写一个函数来判断 t 是否是 s 的字母异位词&#xff08;字母异位词是通过重新排列不同单词或短语的字母而形成的单词或短语&#xff0c;并使用所有原字母一次…...

【Docker基础】容器技术详解:生命周期、命令与实战案例

文章目录 一、什么是容器&#xff1f;二、为什么需要容器三、容器的生命周期容器状态容器OOM容器异常退出容器异常退出容器暂停 四、容器命令命令清单详细介绍 五、容器操作案例容器的状态迁移容器批量操作容器交互模式attached 模式detached 模式interactive 模式 容器 与 宿主…...

电子电气架构 --- 为配备区域计算的下一代电子/电气(E/E)架构

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 周末洗了一个澡,换了一身衣服,出了门却不知道去哪儿,不知道去找谁,漫无目的走着,大概这就是成年人最深的孤独吧! 旧人不知我近况,新人不知我过…...

python基础:位置互换

n int(input()) for _ in range(n):line input().strip()line list(line)for i in range(1,len(line)1):if i%2 0:line[i-2], line[i-1] line[i-1],line[i-2] print(.join(line))以下分不同数据类型说明 Python 实现奇偶互换的方法&#xff1a; 字符串的奇偶位互换 若字…...

51单片机Day03---让一个LED灯闪烁

目录 1.研究原理图&#xff1a; 2.一些小知识&#xff08;重定义的使用&#xff09;&#xff1a; &#xff08;1&#xff09;在单片机中&#xff0c;unsigned int 常用于以下场景&#xff1a; &#xff08;2&#xff09;unsigned char&#xff1a; 3.思路构造&#xff1a;…...

城电科技 | 从概念到落地:如何打造真正的智慧零碳园区?

在科技飞速发展的当下&#xff0c;智慧零碳园区成为了引领未来发展的重要范式。那么&#xff0c;究竟什么是智慧零碳园区呢&#xff1f; 智慧零碳园区&#xff0c;是借助前沿信息技术&#xff0c;把物联网、云计算、大数据等技术深度融入园区管理及产业运营&#xff0c;以此达…...

oracle常见问题处理集锦

oracle常见问题处理集锦 oracle常见问题处理集锦ORA:28000 the count is locked oracle常见问题处理集锦 ORA:28000 the count is locked ORA-28000: 账户已被锁定 这个错误表示你尝试登录的 Oracle 数据库用户账户已被锁定&#xff0c;常见原因包括&#xff1a; 多次密码输错…...

Java-JDBC入门程序、预编译SQL

一. JDBC JDBC&#xff1a;Java DataBase Connectivity 就是使用Java语言操作关系型数据库的一套API 本质&#xff1a;sun公司官方定义一套操作所有关系型数据库的规范&#xff0c;即接口&#xff1b;各个数据库厂商去实现这套接口&#xff0c;提供数据库驱动jar包。我们可以使…...

【SQL】基于多源SQL 去重方法对比 -- 精华版

【SQL】基于SQL 去重方法对比 -- 精华版 一、引言二、基于SQL去重方法完整对比1. MySQL去重方法及优劣势1.1 ​DISTINCT关键字1.2 GROUP BY子句1.3 UNION系列操作1.4 子查询 自关联 2. Hive去重方法及优劣势2.1 DISTINCT关键字2.2 ​GROUP BY子句2.3 ​ROW_NUMBER窗口函数2.4 …...

list的使用以及模拟实现

本章目标 1.list的使用 2.list的模拟实现 1.list的使用 在stl中list是一个链表,并且是一个双向带头循环链表,这种结构的链表是最优结构. 因为它的实现上也是一块线性空间,它的使用上是与string和vector类似的.但相对的因为底层物理结构上它并不像vector是线性连续的,它并没有…...

java继承练习

//创建父类public class Employee {private String id;private String name;private double salary;public Employee() {}public Employee(String id, String name, double salary) {this.id id;this.name name;this.salary salary;}public String getId() {return id;}pu…...

猫咪如厕检测与分类识别系统系列【一】 功能需求分析及猫咪分类特征提取

开发背景 家里养了三只猫咪&#xff0c;其中一只布偶猫经常出入厕所。但因为平时忙于学业&#xff0c;没法时刻关注牠的行为。我知道猫咪的如厕频率和时长与健康状况密切相关&#xff0c;频繁如厕可能是泌尿问题&#xff0c;停留过久也可能是便秘或不适。为了更科学地了解牠的…...

sparkcore编程算子

今天是Spark Core编程算子 Value类型算子 1. map 将处理的数据逐条进行映射转换&#xff0c;这里的转换可以是类型的转换&#xff0c;也可以是值的转换。Map算子是分区内一个数据一个数据的执行&#xff0c;类似于串行操作。 特点&#xff1a; - 主要目的将数据源中的数据进行…...

【EI会议】第三届机器人与软件工程前沿国际会议(FRSE 2025)

第三届机器人与软件工程前沿国际会议&#xff08;FRSE 2025&#xff09;将于2025年8月8日-10日在中国张家界召开。会议由清华大学自动化系主办&#xff0c;长沙理工大学、湖南科技大学、吉首大学、AC学术中心协办。 一、会议信息 大会官网&#xff1a;www.icfrse.org 会议时…...

机器人系统仿真--1.机器人模型URDF

添加机器人地盘...

具身机器人中AI(DEEPSEEK)与PLC、驱动器协同发展研究:突破数据困境与指令精确控制(3)

具身机器人中AI&#xff08;DEEPSEEK&#xff09;与PLC、驱动器协同发展研究&#xff1a;突破数据困境与指令精确控制&#xff08;1&#xff09;-CSDN博客 具身机器人中AI&#xff08;DEEPSEEK&#xff09;与PLC、驱动器协同发展研究&#xff1a;突破数据困境与指令精确控制&a…...

+++++背到厌倦。持续更新

Spring IoC 的工作流程: 读取 BeanDefinition: Spring 容器启动时&#xff0c;会读取 Bean 的配置信息 (例如 XML 配置文件、注解或 Java 代码)&#xff0c;并将这些配置信息转换为 BeanDefinition 对象。创建 Bean 实例: 根据 BeanDefinition 中的信息&#xff0c;Spring 容器…...

修改 docker 工作目录

一、停掉 containerd、cri-docker、docker systemctl stop containerd systemctl stop cri-docker systemctl stop docker 二、拷贝 docker 工作目录下的所有文件到新路径 rsync -aP /var/lib/docker/ /docker/data/ 三、daemon.json 添加新工作目录路径 {"registry-…...

51c嵌入式~继电器~合集1

我自己的原文哦~ https://blog.51cto.com/whaosoft/13775821 一、继电器应用细节 继电器的应用&#xff0c;相信大家都知道&#xff0c;在电路中只要给它供电、断电也就可以工作了。本文讨论它的应用细节。 现在流行的接法 图中&#xff0c;继电器的线圈经过Q1作为开关&am…...

舵机:机器人领域的“关节革命者”

机器人的技术&#xff0c;每一个细微的进步都可能引领一场行业变革。而在这场变革中&#xff0c;舵机作为机器人关节的核心部件&#xff0c;正悄然上演着一场革命性的应用风暴。从简单的关节运动到复杂的姿态控制&#xff0c;舵机以其卓越的性能和无限的可能&#xff0c;重新定…...

飞书集成衡石ChatBot实战:如何10分钟搭建一个业务数据问答机器人?

让数据查询像聊天一样简单 在快节奏的业务环境中&#xff0c;数据查询的实时性和便捷性至关重要。传统BI工具需要复杂的操作&#xff0c;而衡石ChatBot结合飞书&#xff0c;让业务人员只需在聊天窗口提问&#xff0c;就能立刻获取数据反馈&#xff0c;真正实现“零门槛”数据分…...

高并发环境下超发现象的详细分析,包含场景示例、影响分析及解决方案(悲观锁、乐观锁、分布式锁)

以下是针对高并发环境下超发现象的详细分析&#xff0c;包含场景示例、影响分析及解决方案&#xff1a; 高并发下的超发详解 1. 超发现象定义 超发&#xff08;Over-issuance&#xff09;指在并发操作中&#xff0c;系统实际发放的资源&#xff08;如商品库存&#xff09;超过…...

Git 分支整合策略:Cherry-pick、Merge、Rebase 三者之间对比

Git 分支整合策略详解&#xff1a;Cherry-pick、Merge、Rebase 在日常的 Git 多分支协作开发中&#xff0c;代码合并是常见操作。Git 中主要提供以下三种方式来合并或迁移分支的提交&#xff1a; Cherry-pick&#xff1a;精确挑选部分提交复制到当前分支&#xff1b;Merge&am…...