共享記憶體是 System V IPC 中 最高效 的機制:允許多進程將同一物理記憶體頁面映射到各自的虛擬地址空間,實現 零複製 數據交換。
int shmget(key_t key, size_t size, int shmflg);
| 參數 | 說明 |
|---|---|
| key | IPC 鍵值 (ftok 產生或 IPC_PRIVATE) |
| size | 段大小 (位元組),建立時指定,獲取時可為 0 |
| shmflg | 權限 | IPC_CREAT | IPC_EXCL |
返回:共享記憶體標識符 (shmid),失敗返 -1
注意:需自行定義數據結構並計算大小:size = sizeof(MyStruct)
void *shmat(int shmid, const void *shmaddr, int shmflg);
| 參數 | 說明 |
|---|---|
| shmid | shmget 返回的標識符 |
| shmaddr | 指定映射地址 (通常 NULL 讓內核選擇) |
| shmflg | SHM_RDONLY (唯讀) | SHM_RND (地址對齊) | 0 |
返回:映射起始地址 (void*),失敗返 (void*)-1
典型用法:MyStruct *ptr = (MyStruct*)shmat(shmid, NULL, 0);
int shmdt(const void *shmaddr);
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
| 命令 | 功能 | buf 用途 |
|---|---|---|
| IPC_RMID | 標記刪除 (最後一個進程分離後實際釋放) | 忽略 |
| IPC_STAT | 獲取狀態 | 填充 shmid_ds |
| IPC_SET | 設置屬性 | 從 shmid_ds 讀取 |
shmid_ds 關鍵成員 (內核定義 shmid_kernel):
struct shmid_kernel {
struct kern_ipc_perm shm_perm; // 權限/擁有者
struct file *shm_file; // 關聯文件對象
unsigned long shm_nattch; // 當前連接進程數
unsigned long shm_segsz; // 段大小
time_t shm_atim; // 最後 shmat 時間
time_t shm_dtim; // 最後 shmdt 時間
time_t shm_ctim; // 最後 shmctl 時間
pid_t shm_cprid; // 建立者 PID
pid_t shm_lprid; // 最後操作者 PID
// ... 內核內部字段
};
// 1. 定義共享結構
typedef struct {
int data[100];
int count;
int write_pos;
} SharedData;
// 2. 建立/獲取
int shmid = shmget(ftok(".", 's'), sizeof(SharedData), 0666 | IPC_CREAT);
// 3. 映射
SharedData *shm = (SharedData*)shmat(shmid, NULL, 0);
// 4. 寫入數據 (需配合信號量同步!)
sem_p(sem_mutex);
shm->data[shm->write_pos++] = value;
shm->count++;
sem_v(sem_mutex);
// 5. 解除映射 (進程結束前)
shmdt(shm);
// 1. 獲取現有段
int shmid = shmget(ftok(".", 's'), sizeof(SharedData), 0666);
// 2. 映射
SharedData *shm = (SharedData*)shmat(shmid, NULL, SHM_RDONLY); // 或 0
// 3. 讀取數據 (需同步)
sem_p(sem_mutex);
int val = shm->data[read_pos++];
shm->count--;
sem_v(sem_mutex);
// 4. 解除映射
shmdt(shm);
// 僅標記刪除,實際釋放在所有進程 shmdt 後
shmctl(shmid, IPC_RMID, NULL);
# 查看當前限制
cat /proc/sys/kernel/shmmax # 單段最大位元組 (典型 32MB/64MB)
cat /proc/sys/kernel/shmall # 系統總共享頁數
cat /proc/sys/kernel/shmmni # 最大段數 (典型 4096)
# 臨時調大 (需 root)
echo 1073741824 > /proc/sys/kernel/shmmax # 1GB
# 永久生效 (/etc/sysctl.conf)
kernel.shmmax = 1073741824
kernel.shmall = 262144
kernel.shmmni = 4096
| 機制 | 數據複製次數 | 同步內建 | 適用數據量 | 編程複雜度 |
|---|---|---|---|---|
| 共享記憶體 | 0 (零複製) | 否 | 大 (MB~GB) | 高 (需自管同步) |
| 消息隊列 | 2 (用戶↔內核) | 是 | 中 (KB~MB) | 中 |
| 管道/FIFO | 2 (用戶↔內核) | 是 | 小~中 | 低 |
| UNIX Domain Socket | 1-2 (可傳 fd) | 否 | 中~大 | 中 |
// POSIX shm_open (更標準、支援文件系統路徑)
int fd = shm_open("/my_shm", O_CREAT | O_RDWR, 0666);
ftruncate(fd, sizeof(SharedData));
SharedData *ptr = mmap(NULL, sizeof(SharedData),
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
// 使用完成
munmap(ptr, sizeof(SharedData));
shm_unlink("/my_shm");
優勢:路徑名而非 key、語義更清晰、支援 mmap 所有特性 (MAP_LOCKED, MAP_POPULATE 等)
ipcs -m:列出所有共享記憶體段ipcrm -m shmid:手動刪除殘留段cat /proc/sysvipc/shm:內核視角詳細資訊pmap -x PID:查看進程映射的共享段