首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

System V 共享記憶體 IPC 詳解:shmget、shmat、shmdt、shmctl 完整指南

     編輯:關於C語言

共享記憶體核心概念

共享記憶體是 System V IPC 中 最高效 的機制:允許多進程將同一物理記憶體頁面映射到各自的虛擬地址空間,實現 零複製 數據交換。

四大核心函數詳解

shmget - 建立/獲取共享記憶體段

int shmget(key_t key, size_t size, int shmflg);
參數說明
keyIPC 鍵值 (ftok 產生或 IPC_PRIVATE)
size段大小 (位元組),建立時指定,獲取時可為 0
shmflg權限 | IPC_CREAT | IPC_EXCL

返回:共享記憶體標識符 (shmid),失敗返 -1

注意:需自行定義數據結構並計算大小:size = sizeof(MyStruct)

shmat - 映射到進程地址空間

void *shmat(int shmid, const void *shmaddr, int shmflg);
參數說明
shmidshmget 返回的標識符
shmaddr指定映射地址 (通常 NULL 讓內核選擇)
shmflgSHM_RDONLY (唯讀) | SHM_RND (地址對齊) | 0

返回:映射起始地址 (void*),失敗返 (void*)-1

典型用法:MyStruct *ptr = (MyStruct*)shmat(shmid, NULL, 0);

shmdt - 解除映射

int shmdt(const void *shmaddr);
  • 參數:shmat 返回的地址
  • 僅解除當前進程映射,不刪除 共享記憶體段
  • 返回:成功 0,失敗 -1

shmctl - 控制操作

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);

關鍵注意事項

  1. 必須配合同步機制:共享記憶體本身無同步,需搭配信號量/互斥鎖
  2. 結構體對齊:不同架構/編譯器對齊規則可能不同,建議显式打包
  3. 生命週期管理:IPC_RMID 只是標記,實際釋放需所有進程分離
  4. 權限檢查:shmflg 權限位需與進程有效 UID/GID 匹配
  5. 系統限制:SHMMAX (單段最大)、SHMALL (系統總頁數)、SHMMNI (段數上限)

系統限制查看與調優

# 查看當前限制
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

與其他 IPC 效能對比

機制數據複製次數同步內建適用數據量編程複雜度
共享記憶體0 (零複製)否大 (MB~GB)高 (需自管同步)
消息隊列2 (用戶↔內核)是中 (KB~MB)中
管道/FIFO2 (用戶↔內核)是小~中低
UNIX Domain Socket1-2 (可傳 fd)否中~大中

現代替代:mmap + POSIX 共享記憶體

// 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:查看進程映射的共享段
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved