萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 作業系統 >  >> Linux 教學

Unix/Linux 共享記憶體機制:POSIX vs System V API 深度比較

共享記憶體是最高效的進程間通訊 (IPC) 機制,避免核心態/用戶態資料拷貝。Unix 提供 POSIX (mmap) 與 System V (shmget) 兩套 API,各有特性與適用場景。

核心優勢

  • 零拷貝:進程直接讀寫同一物理記憶體頁面
  • 隨核心持久:System V 段隨核心存續;POSIX 支援檔案備份持久化
  • 同步需自行處理:需搭配 mutex、semaphore、futex、seqlock

POSIX 共享記憶體 (shm_open + mmap)

API 流程


#include 
#include 
#include 

// 1. 建立/開啟共享記憶體物件 (實際為 /dev/shm/ 下檔案)
int fd = shm_open("/my_shm", O_CREAT | O_RDWR, 0640);
if (fd == -1) errExit("shm_open");

// 2. 設定大小 (可動態調整)
if (ftruncate(fd, 4096) == -1) errExit("ftruncate");

// 3. 映射至進程位址空間
void *addr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) errExit("mmap");

// 4. 使用記憶體 (addr 視為 char* 或結構體指標)
struct shared_data *data = (struct shared_data *)addr;
pthread_mutex_init(&data->mutex, NULL);  // 需 PTHREAD_PROCESS_SHARED

// 5. 解除映射與關閉
munmap(addr, 4096);
close(fd);

// 6. 刪除物件 (最後一個引用關閉後實際移除)
shm_unlink("/my_shm");

關鍵特性

  • 大小可透過 ftruncate 動態調整
  • 支援 msync(MS_SYNC|MS_ASYNC) 強制刷回底層檔案 (持久化)
  • 名字空間在 /dev/shm/ (tmpfs),重開機消失除非掛載持久化儲存
  • 可對應實體檔案 (open() 取代 shm_open()) 實現記憶體映射檔

System V 共享記憶體 (shmget + shmat)

API 流程


#include 
#include 

// 1. 建立/取得段 (key_t 由 ftok 產生或 IPC_PRIVATE)
key_t key = ftok("/tmp/shm_key", 65);
int shmid = shmget(key, 4096, IPC_CREAT | 0640);
if (shmid == -1) errExit("shmget");

// 2. 連接至進程位址空間
void *addr = shmat(shmid, NULL, 0);
if (addr == (void *)-1) errExit("shmat");

// 3. 使用記憶體
struct shared_data *data = (struct shared_data *)addr;

// 4. 脫離
shmdt(addr);

// 5. 控制/刪除
shmctl(shmid, IPC_RMID, NULL);  // 標記刪除,最後脫離時移除
shmctl(shmid, IPC_STAT, &buf);  // 取得狀態

關鍵限制

  • 段大小建立時固定,不可調整 (需刪除重建)
  • 系統級限制:SHMMAX (單段最大)、SHMALL (總頁數)、SHMMNI (段數量)
  • 可透過 /proc/sys/kernel/shmmax、shmall、shmmni 動態調整
  • 無原生持久化機制,重開機遺失

兩套 API 比較總表

維度POSIX (shm_open/mmap)System V (shmget/shmat)
標準POSIX.1-2001System V IPC (舊標準)
命名空間檔案系統路徑 (/dev/shm/name)整數 Key (ftok 產生)
大小調整動態 (ftruncate)靜態 (建立時固定)
持久化msync 同步至檔案 / 映射實體檔無 (僅核心持久)
同步原語pthread_mutex/cond (PROCESS_SHARED)semget/semop (獨立信號燈集)
權限控制標準檔案權限 (uid/gid/mode)ipc_perm 結構 (uid/gid/mode)
可移植性現代 Unix/Linux 完整支援傳統 Unix 相容性佳
除錯可見性ls /dev/shm/ 直接可見ipcs -m 查看

選擇建議

  • 新專案、需動態大小、持久化、POSIX 同步 → POSIX shm_open + mmap
  • 舊代碼維護、嵌入式無 /dev/shm、需 System V 信號燈配合 → System V shmget
  • 高效能記憶體映射檔 (資料庫、日誌) → mmap 映射實體檔案 (MAP_SHARED)
  • 大型共享緩衝區 (>SHMMAX) → POSIX 多段陣列或 System V 陣列 (shmget 多次)

同步機制整合範例 (POSIX)


struct shared_buf {
    pthread_mutex_t mutex;
    pthread_cond_t  cond;
    size_t          len;
    char            data[4096];
};

// 初始化 (僅建立者執行)
pthread_mutexattr_t mattr;
pthread_mutexattr_init(&mattr);
pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(&shm->mutex, &mattr);
pthread_condattr_t cattr;
pthread_condattr_init(&cattr);
pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
pthread_cond_init(&shm->cond, &cattr);

// 生產者
pthread_mutex_lock(&shm->mutex);
while (shm->len == sizeof(shm->data)) pthread_cond_wait(&shm->cond, &shm->mutex);
memcpy(shm->data, src, n);
shm->len = n;
pthread_cond_signal(&shm->cond);
pthread_mutex_unlock(&shm->mutex);

// 消費者
pthread_mutex_lock(&shm->mutex);
while (shm->len == 0) pthread_cond_wait(&shm->cond, &shm->mutex);
process(shm->data, shm->len);
shm->len = 0;
pthread_cond_signal(&shm->cond);
pthread_mutex_unlock(&shm->mutex);

理解兩套 API 的設計哲學與限制,配合適當同步原語,能構建高效、可靠的進程間資料交換通道。

copyright © 萬盛學電腦網 all rights reserved