程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> PHP基礎知識 >> php 共享內存和消息隊列的簡單實現

php 共享內存和消息隊列的簡單實現

編輯:PHP基礎知識
 

php作為腳本程序,通常生命周期都很短,如在web應用中,一次請求就是php運行的一個周期,請求結束則生命周期截止。所以php在處理需要共 享的資源時,一般會將共享數據保存在數據庫或dbm之類的文件中,再者就是利用內存實現共享。你可以選擇已有的工具輔助你,像memcache;也可以自己編寫代碼訪問操作系統的共享內存段。
php中對共享內存段的操作有兩組函數:System V IPC和Shared Memory。 其中System V IPC系列函數能夠更方便的操作數據,無需像Shared Memory那樣必須自己掌握讀寫時的偏移量、長度等,也不用序列化/反序列化來回轉換(因為Shared Memory函數只支持字符串格式的數據參數)。但是System V IPC系列不支持Windows,所以如果要在win環境下使用,只能選Shared Memory。
因為php默認不支持這些函數,所以需要重編譯php。如要使用:
System V信號量,編譯時加上 –enable-sysvsem
System V共享內存,編譯時加上 –enable-sysvshm
System V消息隊列,編譯時加上 –enable-sysvmsg
Shared Memory,編譯時加上 –enable-shmop

在相關的函數組中,比較常用的有以下幾個。

ftok ( string $pathname , string $proj )
手冊上給出的解釋是:Convert a pathname and a project identifier to a System V IPC key。這個函數返回的鍵值唯一對應linux系統中一個消息隊列。在獲得消息隊列的引用之前都需要調用這個函數。

msg_get_queue ( int $key [, int $perms ] )
msg_get_queue() 會根據傳入的鍵值返回一個消息隊列的引用。如果linux系統中沒有消息隊列與鍵值對應,msg_get_queue()將會創建一個新的消息隊列。函數 的第二個參數需要傳入一個int值,作為新創建的消息隊列的權限值,默認為0666。這個權限值與linux命令chmod中使用的數值是同一個意思,因為在linux系統中一切皆是文件。

msg_send ( resource $queue , int $msgtype , mixed $message [, bool $serialize [, bool $blocking [, int &$errorcode ]]] )
顧名思義,該函數用來向消息隊列中寫數據。

msg_stat_queue ( resource $queue )
這個函數會返回消息隊列的元數據。消息隊列元數據中的信息很完整,包括了消息隊列中待讀取的消息數、最後讀寫隊列的進程ID等。示例代碼在第8行調用該函數返回的數組中隊列中待讀取的消息數msg_qnum值為0。

msg_receive ( resource $queue , int $desiredmsgtype , int &$msgtype , int $maxsize , mixed &$message [, bool$unserialize [, int $flags [, int &$errorcode ]]] )
msg_receive用於讀取消息隊列中的數據。

msg_remove_queue ( resource $queue )
msg_remove_queue用於銷毀一個隊列。

先看個Shared Memory的例子:

 


<?php
$key = ftok(__FILE__, ‘m’);//ftok將一個路徑名pathname和一個項目名(必須為一個字符), 轉化成一個整形的用來使用系統V IPC的key
$size = 100;//開辟的共享內存空間
$shm_h = @shmop_open($key, ‘c’, 0644, $size);
if($shm_h === false) {
echo “shmop open failed”;
exit;
}
$data = shmop_read($shm_h, 0, $size);
$data = unserialize($data);
//如果沒有數據則寫一個
if(empty($data)) {
echo “現在沒有數據“;
$data = “我是寫入共享內存的數據“;
//就算數據是文本,write時也要序列化
$write_size = shmop_write($shm_h, serialize($data), 0);
if($write_size === false) echo “shmop write failed!”;
}
//如果有,顯示出來,之後刪掉
else {
echo “寫入共享內存的數據: “;
print_r($data);
shmop_delete($shm_h);
}
shmop_close($shm_h);
?>

再看一個個System V shm的例子:

 

<?php
$shm_key = ftok(__FILE__, ‘m’);
$memsize = 120;
$shm_h = shm_attach($shm_key, $memsize, 0644);
if($shm_h === false) {
echo “shmop open failed”;
exit;
}
$var_key = 3;
$data = @shm_get_var($shm_h, $var_key);
if(empty($data)) {
$data = “我是寫入共享內存的數據“;
echo “寫入之前沒有數據, 現在插入 $data.\n”;
shm_put_var($shm_h, $var_key, $data);
} else {
echo “find data: $data\n”;
shm_remove_var($shm_h, $var_key);
}
shm_detach($shm_h);
?>

 

可以看到,sysV對於每個數據都另外設立了對應的var_key,這樣在同一內存區域可以保存多個數據,而不用像shmop中那樣再申請另外一個共享內存區域,還免除了序列化的干擾(雖然數據最終還是以序列化的形式保存,但不用開發者去手動實現)。
PS:

(1)不管是shm_attach還是shmop_open,所申請的內存的大小一定要滿足後面數據的體積,這 個體積包括數據本身序列化後的長。還有php添加的少量header信息(因為頭信息會占用內存,具體解釋可以參照這個技術博客:http://www.laruence.com/2011/03/04/1894.html (誰動了我的內存,國內一個PHP大牛關於PHP內存方面的解釋))。

(2)還有就是為了防止共享內存被浪費,當數據無用時及時調用對應的remove方法釋放資源。

php官方文檔中有人提出了一種計算要申請的內存大小的公式,這個公式可以保證 所申請的內存足夠存儲一個指定的數據。公式如下:

$shmHeaderSize = (PHP_INT_SIZE * 4) + 8;//當shm_put_var調用時,php會在序列化後的數據前面,加一個header

$shmVarSize = (((strlen(serialize($foo))+ (4 * PHP_INT_SIZE)) /4 ) * 4 ) + 4;

$memsize = $shmHeaderSize + $shmVarSize;

在System V IPC函數組中,消息隊列似乎可以視為另一種共享內存,只是數據存儲的方式有些不同。簡單來說,就是每個key對應一個隊列,每個隊列可以保存多個數據,數據間按照先進先出的原則進行操作。

看一個消息隊列的例子:

 

<?php
if ( sizeof($argv)<2 ) {
echo “Usage: $argv[0] stat|send|receive|remove msgType MSG [msg] \n\n” ;
echo ” EX: $argv[0] send 1 \”This is no 1\” \n” ;
echo ” $argv[0] receive ID \n” ;
echo ” $argv[0] stat \n” ;
echo ” $argv[0] remove \n” ;
exit;
}

$MSGKey = “123456” ;
$seg = msg_get_queue($MSGKey) ;

switch ( $argv[1] ) {
case “send”:
msg_send($seg, $argv[2], $argv[3]);
echo “msg_send done…\n” ;
break;

case “receive”:
$stat = msg_stat_queue( $seg );
echo ‘Messages in the queue: ‘.$stat[‘msg_qnum’].”\n”;
if ( $stat[‘msg_qnum’]>0 ) {
msg_receive($seg, $argv[2], $msgtype, 1024, $data, true, MSG_IPC_NOWAIT);
var_dump($msgtype);
var_dump($data);
echo “\n”;
}
else {
echo “No Msg…\n”;
}
break;

case “stat”:
print_r( msg_stat_queue($seg) );
break;

case “remove”:
msg_remove_queue($seg);
break;
}
?>  

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved