在單台服務器上,多個 PHP 進程(如 CLI Worker、Web 請求、定時任務)需要高性能數據共享時,系統 V 共享內存 + 信號量是原生、高效、無外部依賴的方案。本文完整講解 shmop 操作、信號量同步、實戰封裝。
# PHP 編譯安裝必須啟用 ./configure --enable-shmop --enable-sysvsem --enable-sysvshm # 驗證 php -m | grep -E 'shmop|sysv'
| 概念 | 函數族 | 作用 |
|---|---|---|
| 共享內存 | shmop_* | 進程間數據存儲,字節級讀寫 |
| 信號量 | sem_* | 互斥鎖,保證併發安全 |
| 消息隊列 | msg_* | 進程間通信(本文不展開) |
$key = ftok(__FILE__, 's'); // 邏輯鍵值,同鍵值進程共享同一段
$mode = 'c'; // c=創建, a=訪問
$perm = 0644; // 權限
$size = 1024 * 1024; // 1MB
$shmId = shmop_open($key, $mode, $perm, $size);
if (!$shmId) {
die("創建共享內存失敗");
}
// 獲取系統 IPC 狀態:ipcs -m
$data = "Hello Shared Memory " . microtime(true); $bytes = shmop_write($shmId, $data, 0); // 偏移 0 開始寫入 echo "寫入 $bytes 字節";
$data = shmop_read($shmId, 0, $bytes); // 從 0 讀取 $bytes 長度 echo "讀取: $data";
shmop_delete($shmId); // 標記刪除,最後一個進程 detach 時真正釋放 shmop_close($shmId); // 關閉當前進程連接
$semKey = ftok(__FILE__, 'm'); // 不同於 shm key $semId = sem_get($semKey, 1, 0644); // max_acquire=1 互斥鎖 // 獲取鎖(阻塞等待) sem_acquire($semId); // 釋放鎖 sem_release($semId);
class SharedMemory {
private $shmId;
private $semId;
private $size;
public function __construct($name, $size = 1048576) { // 默認 1MB
$this->size = $size;
$key = ftok(__FILE__, $name[0]);
$semKey = ftok(__FILE__, $name[1] ?? 'm');
$this->shmId = shmop_open($key, 'c', 0644, $size);
$this->semId = sem_get($semKey, 1, 0644);
if (!$this->shmId || !$this->semId) {
throw new Exception("IPC 資源創建失敗");
}
}
// 寫入(自動加鎖、序列化)
public function write($key, $value) {
$this->acquire();
try {
$data = $this->readAll();
$data[$key] = $value;
$serialized = serialize($data);
if (strlen($serialized) > $this->size) {
throw new Exception("數據超過共享內存大小");
}
shmop_write($this->shmId, $serialized, 0);
} finally {
$this->release();
}
}
// 讀取(自動加鎖、反序列化)
public function read($key, $default = null) {
$this->acquire();
try {
$data = $this->readAll();
return $data[$key] ?? $default;
} finally {
$this->release();
}
}
// 批量讀取所有
public function readAll() {
$this->acquire();
try {
$raw = shmop_read($this->shmId, 0, $this->size);
$data = @unserialize($raw);
return is_array($data) ? $data : array();
} finally {
$this->release();
}
}
// 刪除鍵
public function delete($key) {
$this->acquire();
try {
$data = $this->readAll();
unset($data[$key]);
shmop_write($this->shmId, serialize($data), 0);
} finally {
$this->release();
}
}
private function acquire() {
sem_acquire($this->semId);
}
private function release() {
sem_release($this->semId);
}
public function __destruct() {
if ($this->shmId) shmop_close($this->shmId);
}
}
// 使用示例
$shm = new SharedMemory('myapp');
// 進程 A 寫入
$shm->write('counter', 0);
for ($i=0; $i<1000; $i++) {
$val = $shm->read('counter');
$shm->write('counter', $val + 1);
}
// 進程 B 讀取
echo $shm->read('counter'); // 1000
class AtomicCounter {
private $shmId;
private $semId;
private $offset;
public function __construct($name, $offset = 0) {
$key = ftok(__FILE__, $name[0]);
$semKey = ftok(__FILE__, $name[1] ?? 'm');
$this->shmId = shmop_open($key, 'c', 0644, 8); // 8 字節存 int64
$this->semId = sem_get($semKey, 1, 0644);
$this->offset = $offset * 8;
}
public function increment($step = 1) {
sem_acquire($this->semId);
try {
$binary = shmop_read($this->shmId, $this->offset, 8);
$value = unpack('Q', $binary)[1] ?? 0;
$value += $step;
shmop_write($this->shmId, pack('Q', $value), $this->offset);
return $value;
} finally {
sem_release($this->semId);
}
}
public function get() {
sem_acquire($this->semId);
try {
$binary = shmop_read($this->shmId, $this->offset, 8);
return unpack('Q', $binary)[1] ?? 0;
} finally {
sem_release($this->semId);
}
}
}
# 查看所有共享內存段 ipcs -m # 查看信號量 ipcs -s # 刪除殘留資源 (謹慎!) ipcrm -mipcrm -s # 關鍵參數調優 (/etc/sysctl.conf) kernel.shmmax = 134217728 # 最大段 128MB kernel.shmall = 32768 # 總頁數 kernel.sem = 250 32000 100 128 # 信號量限制
| 方案 | 性能 | 持久化 | 跨進程 | 網絡透明 | 複雜度 |
|---|---|---|---|---|---|
| shmop + sem | 極高 (內存級) | 進程存活期 | ✅ | ❌ | 中 |
| Redis | 高 (網絡) | 持久化可選 | ✅ | ✅ | 低 |
| 文件鎖 flock | 低 (磁盤) | 持久 | ✅ | ❌ | 低 |
| 數據庫 | 中 | 持久 | ✅ | ✅ | 低 |
| APCu | 極高 | 請求級/進程級 | 同進程池 | ❌ | 極低 |
ftok() 避免衝突,不要硬編碼數字try/finally 保證釋放鎖shmop_open 不支持擴容shmop_close,定期 ipcs 檢查殘留更多高併發技術:《PHP 多進程編程實戰》、《Linux IPC 編程指南》