bcu_shared.c 共享内存操作梳理
仅梳理 SQlite/bcu_shared.c 中 POSIX 共享内存的创建、初始化、互斥锁配置及清理全流程。
一、涉及的全部共享内存 API
| API | 次数 | 作用 |
|---|---|---|
shm_open | 1 | 创建/打开共享内存,返回文件描述符 |
ftruncate | 1 | 设定共享内存大小 |
mmap | 1 | 将共享内存映射到进程地址空间 |
memcpy | 1 | 将数据库数据整体复制进共享内存 |
pthread_mutexattr_init | 1 | 初始化互斥锁属性 |
pthread_mutexattr_setpshared | 1 | 设置属性为"进程间共享" |
pthread_mutex_init | 1 | 在共享内存中初始化互斥锁 |
pthread_mutexattr_destroy | 1 | 销毁互斥锁属性 |
munmap | 1 | 解除内存映射(代码存在但不可达) |
shm_unlink | 1 | 删除共享内存(代码存在但不可达) |
二、操作时序
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 的唯一标识 - 常驻不退出:用死循环维持共享内存生命周期,代价是清理代码不可达