共享記憶體是最高效的進程間通訊 (IPC) 機制,避免核心態/用戶態資料拷貝。Unix 提供 POSIX (mmap) 與 System V (shmget) 兩套 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()) 實現記憶體映射檔
#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 動態調整| 維度 | POSIX (shm_open/mmap) | System V (shmget/shmat) |
|---|---|---|
| 標準 | POSIX.1-2001 | System 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 查看 |
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 的設計哲學與限制,配合適當同步原語,能構建高效、可靠的進程間資料交換通道。