共享内存操作梳理
2026/6/23 19:45:08 网站建设 项目流程

bcu_shared.c 共享内存操作梳理

仅梳理 SQlite/bcu_shared.c 中 POSIX 共享内存的创建、初始化、互斥锁配置及清理全流程。


一、涉及的全部共享内存 API

API次数作用
shm_open1创建/打开共享内存,返回文件描述符
ftruncate1设定共享内存大小
mmap1将共享内存映射到进程地址空间
memcpy1将数据库数据整体复制进共享内存
pthread_mutexattr_init1初始化互斥锁属性
pthread_mutexattr_setpshared1设置属性为"进程间共享"
pthread_mutex_init1在共享内存中初始化互斥锁
pthread_mutexattr_destroy1销毁互斥锁属性
munmap1解除内存映射(代码存在但不可达)
shm_unlink1删除共享内存(代码存在但不可达)

二、操作时序

shm_open() │ ▼ ftruncate() │ ▼ mmap() │ ▼ memcpy() ← 把从 SQLite 加载的5张表数据整体拷入共享内存 │ ▼ pthread_mutexattr_init() │ ▼ pthread_mutexattr_setpshared() ← 关键:设为 PTHREAD_PROCESS_SHARED │ ▼ pthread_mutex_init() │ ▼ while(1) { sleep(1); } ← 常驻,保持共享内存存活 ▼ (不可达) munmap() + shm_unlink() ← 正常清理逻辑,但死循环导致永远走不到

三、逐步骤详解

1.shm_open— 创建共享内存

shm_fd=shm_open("/bcu_shared_data",O_CREAT|O_RDWR,0666);
参数含义
"/bcu_shared_data"共享内存名称,其他进程通过同一个名字 attach
O_CREAT | O_RDWR不存在则创建,以读写方式打开
0666权限位,所有用户可读写

返回值是文件描述符(整数),在 Linux 上位于/dev/shm/bcu_shared_data

注意:此时共享内存大小为 0,还不能用。

2.ftruncate— 设定大小

ftruncate(shm_fd,sizeof(ShareData));

把共享内存"撑大"到sizeof(ShareData)字节。ShareData结构体包含 6 个 Sheet(每个放 50 个PointValue)+pthread_mutex_t,大约 26KB。

失败则close(shm_fd)并退出。

3.mmap— 映射到进程地址空间

g_shared=mmap(NULL,sizeof(ShareData),PROT_READ|PROT_WRITE,MAP_SHARED,shm_fd,0);
参数含义
NULL内核自动选择映射地址
sizeof(ShareData)映射大小
PROT_READ | PROT_WRITE可读可写
MAP_SHARED关键标志:修改对所有进程可见
shm_fd文件描述符
0偏移量,从头开始

成功返回指向共享内存的指针g_shared,之后可以像普通内存一样读写,修改会同步到所有 attach 的进程。

4.memcpy— 整体复制数据

memcpy(g_shared,&temp,sizeof(ShareData));

在此之前,load_sheet_data()已将 5 张 SQLite 表加载到栈上的临时变量ShareData temp。这一步一次性整体拷贝进共享内存,所有后续进程看到的就是这份数据。

5. 互斥锁初始化 — 进程间同步

pthread_mutexattr_tmutex_attr;pthread_mutexattr_init(&mutex_attr);pthread_mutexattr_setpshared(&mutex_attr,PTHREAD_PROCESS_SHARED);pthread_mutex_init(&g_shared->mutex,&mutex_attr);pthread_mutexattr_destroy(&mutex_attr);

这是一个四步走的标准流程:

步骤函数说明
创建属性对象pthread_mutexattr_init在栈上初始化属性结构体
设为进程间共享pthread_mutexattr_setpshared核心:不加这个标志,锁只在单进程内有效
初始化锁pthread_mutex_init在共享内存上创建锁,所有进程可用
销毁属性pthread_mutexattr_destroy栈上的属性对象用完释放

之后其他进程(如rtu)使用LOCK(&gbcu_shared->mutex)/UNLOCK(...)即可跨进程同步。

6. 常驻循环

while(1){sleep(1);}

唯一目的:保持进程存活。一旦进程退出,共享内存虽然不会立即消失(还有进程 attach 的话),但创建者退出意味着无人负责清理。

7. 清理逻辑(不可达)

munmap(g_shared,sizeof(ShareData));// 解除映射close(shm_fd);// 关闭文件描述符shm_unlink("/bcu_shared_data");// 删除共享内存

信号处理已注释(signal(SIGINT, ...)被注释掉),while(1)死循环使得这段代码永不执行。进程只能被kill强制终止。


四、其他进程如何 attach

rtu.c等进程通过相同 API 附加同一块共享内存:

// rtu.c 中对应的操作(不在本文件,仅做对照)shm_fd=shm_open("/bcu_shared_data",O_RDWR,0);// 名称必须一致gbcu_shared=mmap(NULL,sizeof(ShareData),PROT_READ|PROT_WRITE,MAP_SHARED,shm_fd,0);// 使用LOCK(&gbcu_shared->mutex);get_bcu_shared_point_value("系统状态管理、开关控制",120,&val);UNLOCK(&gbcu_shared->mutex);

名称"/bcu_shared_data"是连接双方的关键约定。


五、总结

bcu_shared.c的共享内存操作遵循 POSIX 标准五步走:

shm_open → ftruncate → mmap → 写入数据 → 初始化进程间锁 → 常驻

设计要点:

  • 一次搬运:SQLite → 临时结构体 →memcpy进共享内存,之后再也不碰数据库
  • 进程间锁PTHREAD_PROCESS_SHARED保证多个进程可以安全地并发读写
  • 名称约定"/bcu_shared_data"是跨进程 attach 的唯一标识
  • 常驻不退出:用死循环维持共享内存生命周期,代价是清理代码不可达

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询