共享內存(Shared Memory, shm)是最快的進程間通信(IPC)方式:映射同一物理頁到多個進程虛擬地址空間,實現零拷貝數據直讀寫。本文以完整父子進程示例,演示 shmget/shmat/shmdt/shmctl 全生命週期,並解析地址映射關係、同步機制缺失風險、持久化特性與 ipcs 監控。
#include "apue.h"
#include
#define SHM_SIZE 100000
#define SHM_MODE 0600
int main(void) {
int shmid, pid;
char *shmptr;
key_t key;
// 1. 生成 IPC key
if ((key = ftok("sharem.c", 1)) == -1)
err_sys("ftok error");
// 2. 創建/獲取共享內存段
if ((shmid = shmget(key, SHM_SIZE, IPC_CREAT | SHM_MODE)) == -1)
err_sys("shmget error");
// 3. fork 子進程
if ((pid = fork()) < 0)
err_sys("fork error");
if (pid == 0) { // ========== 子進程:寫入 ==========
if ((shmptr = shmat(shmid, 0, 0)) == (void *)-1)
err_sys("child shmat error");
printf("child shm attached: %lx ~ %lx\n",
(unsigned long)shmptr, (unsigned long)shmptr + SHM_SIZE);
printf("child input: ");
scanf("%s", shmptr); // 從標準輸入讀字符串寫入共享內存
_exit(0);
} else { // ========== 父進程:讀取 ==========
sleep(5); // 簡易同步:等子進程寫完(實際應用需信號量/記錄鎖)
if ((shmptr = shmat(shmid, 0, 0)) == (void *)-1)
err_sys("parent shmat error");
printf("parent shm attached: %lx ~ %lx\n",
(unsigned long)shmptr, (unsigned long)shmptr + SHM_SIZE);
printf("parent output: %s\n", shmptr);
// 父進程結束,共享內存仍保留(持久化)
}
return 0;
}
child shm attached: 7f8b9c000000 ~ 7f8b9c0186a0
parent shm attached: 7f8b9c000000 ~ 7f8b9c0186a0
child input: hello_world
parent output: hello_world
0x7f8b9c000000 起)。| 函數 | 作用 | 關鍵參數 |
|---|---|---|
ftok(path, id) | 生成 IPC key | 路徑需存在,id 為 0-255 |
shmget(key, size, flags) | 創建/獲取段 | IPC_CREAT|0600,返回 shmid |
shmat(shmid, addr, flag) | 掛接到進程空間 | addr=0 由內核選址,返回虛擬地址 |
shmdt(shmptr) | 分離映射 | 不刪除段,僅解除映射 |
shmctl(shmid, IPC_RMID, 0) | 標記刪除段 | 最後一個分離進程退出時真正釋放物理頁 |
示例中 sleep(5) 純屬演示簡化。生產環境必須使用:
semget/semop 或 POSIX sem_t):PV 操作互斥/同步。fcntl F_SETLKW):對共享內存文件描述符加鎖(需 shm_open 路徑)。shmctl IPC_RMID 或重啟。ipcs -m 查看所有段:shmid、perms、bytes、nattch(掛接進程數)、pid(創建者/最後操作者)。ipcrm -m shmid 手動刪除。int fd = shm_open("/my_shm", O_CREAT|O_RDWR, 0600);
ftruncate(fd, SHM_SIZE);
void *ptr = mmap(NULL, SHM_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
// 使用 ptr 讀寫
munmap(ptr, SHM_SIZE);
shm_unlink("/my_shm");
優勢:路徑名命名、支援 fcntl 鎖、統一 mmap 接口、可用 ls /dev/shm/ 直觀查看。
共享內存以物理頁多映射實現零拷貝高速 IPC,核心 API 四部曲:ftok → shmget → shmat → shmdt/shmctl。
必記三點:
1. 跨進程存偏移量,不存指針。
2. 必須額外加同步(信號量/鎖),否則數據競爭。
3. 段持久化,及時 IPC_RMID 或 shm_unlink 避免洩漏。
掌握此機制,是高性能多進程架構(如 Nginx 共享字典、Redis 共享內存緩存、數據庫共享緩衝池)的基石。