在單獨的一個PHP進程中讀寫、建立、刪除共享內存方面上你應該沒有問題了。可是實際運行中不可能只是一個PHP進程在運行中。若是在多個進程的狀況下你仍是沿用單個進程的處理方法,你必定會碰到問題--著名的並行和互斥問題。好比說有2個進程同時須要對同一段內存進行讀寫。當兩個進程同時執行寫入操做時,你將獲得一個錯誤的數據,由於該段內存將之多是最後執行的進程的內容,甚至是由2個進程寫入的數據輪流隨機出現的一段混合的四不象。這顯然是不能接受的。爲了解決這個問題,咱們必須引入互斥機制。互斥機制在不少操做系統的教材上都有專門講述,這裏很少重複。實現互斥機制的最簡單辦法就是使用信號燈。信號量是另一種進程間(IPC)的方式,它同其餘IPC機構(管道、FIFO、消息隊列)不一樣。php
說到信號量可能你們都很陌生,做爲php確定知道mysql、redis中的鎖,固然還有php文件鎖。說白了就是鎖,用來解決進程(線程同步的問題),訪問前獲取鎖(獲取不到則等待),訪問後釋放鎖。html
信號量的做用就是,考慮是否有多個進程同時寫入數據到共享內存的狀況,是否須要避免衝突。mysql
舉一個生活中的例子:以一個停車場的運做爲例。簡單起見,假設停車場只有三個車位,一開始三個車位都是空的。這時若是同時來了五輛車,看門人容許其中三輛直接進入,而後放下車攔,剩下的車則必須在入口等待,此後來的車也都不得不在入口處等待。這時,有一輛車離開停車場,看門人得知後,打開車攔,放入外面的一輛進去,若是又離開兩輛,則又能夠放入兩輛,如此往復。在這個停車場系統中,車位是公共資源,每輛車比如一個線程,看門人起的就是信號量的做用。nginx
記得給環境開啓兩個擴展【enable-shmop --enable-sysvsem】redis
由於php默認不支持這些函數,因此須要重編譯php。如要使用:
System V信號量,編譯時加上 –enable-sysvsem
System V共享內存,編譯時加上 –enable-sysvshm
System V消息隊列,編譯時加上 –enable-sysvmsg
Shared Memory,編譯時加上 –enable-shmopsql
信號量系列函數函數
<?php //一、建立信號量惟一標識符 $key = 0x4337b101; //二、建立信號量資源ID $sem_resouce_id = sem_get($key); //三、接受信號量 sem_acqure($sem_resource_id); //四、釋放信號量 sem_release($sem_resource_id); //五、銷燬信號量 sem_remove($sem_resource_id);
簡單小案例ui
<?php $key = 0x4337b101; $sem_id = sem_get($key); //請求信號控制權 if (sem_acquire($sem_id)) { $shm_id = shmop_open($key, 'c', 0644, 1024); //讀取並寫入數據 $count = (int) shmop_read($shm_id, 0, 8) + 1; shmop_write($shm_id, str_pad($count, 8, '0', STR_PAD_LEFT), 0); // echo shmop_read($shm_id, 0, 8); //關閉內存塊 shmop_close($shm_id); //釋放信號 sem_release($sem_id); }
若是出現報錯:Warning: sem_release(): SysV semaphore 140680297324568 (key 0x4337b101) is not currently acquired in /usr/local/nginx/html/index.php on line 38spa
那是由於沒有得到鎖~操作系統
在Linux下命令觀察,查看系統共享內存,信號量,隊列
# ipcs
# ipcs -s //單獨查看信號量的話,使用ipcs -s命令
稍微複雜的案例
<?php //建立共享內存區域 $shm_key = ftok(__FILE__, 'a'); $shm_id = shm_attach($shm_key, 1024, 0755); //var_dump($shm_id);die(); resource(4) of type (sysvshm) const SHARE_KEY = 1; $child_list = []; //加入信號量 $sem_id = ftok(__FILE__, 'b'); $signal = sem_get($sem_id); //$signal resource(5) of type (sysvsem) for ($i = 0; $i < 3; $i++) { $pid = pcntl_fork(); if ($pid == -1) { exit("Fork fail!".PHP_EOL); } elseif ($pid == 0) { //獲取信號量 sem_acquire($signal); if (shm_has_var($shm_id,SHARE_KEY)) { $count = shm_get_var($shm_id, SHARE_KEY); $count++; //模擬業務處理 $sec = rand(1, 3); sleep($sec); shm_put_var($shm_id, SHARE_KEY, $count); } else { $count = 0; $sec = rand(1, 3); sleep($sec); shm_put_var($shm_id, SHARE_KEY, $count); } echo "child process: ".getmypid()." is writing! now count is: $count ".PHP_EOL; //釋放信號量 sem_release($signal); exit("child process".getmypid()."end".PHP_EOL); } else { $child_list[] = $pid; } } while (count($child_list) > 0) { foreach ($child_list as $key => $pid) { $status = pcntl_waitpid($pid, $status); if ($status > 0 || $status == -1) { unset($child_list[$key]); } } sleep(1); } $count = shm_get_var($shm_id, SHARE_KEY); echo " $count ".PHP_EOL; //銷燬信號量 sem_remove($signal); shm_remove($shm_id); shm_detach($shm_id);
實際運用中根據場景靈活運用就能夠了~