共有メモリとメッセージキューに対する PHP 操作

WBOY
リリース: 2016-06-20 13:04:46
オリジナル
1086 人が閲覧しました

PHP における共有メモリとメッセージキューの操作

スクリプト プログラムとして、PHP のライフ サイクルは通常短いです。たとえば、Web アプリケーションでは、1 つのリクエストが PHP の実行サイクルとなり、リクエストが終了するとライフ サイクルが終了します。したがって、PHP が共有する必要があるリソースを処理するときは、通常、共有データをデータベースや dbm などのファイルに保存し、メモリを使用して共有を実現します。 memcache などの既存のツールを選択して、オペレーティング システムの共有メモリ セグメントにアクセスする独自のコードを作成することもできます。

PHP で共有メモリ セグメントを操作するための関数には、SystemVIPC と SharedMemory の 2 つのセットがあります。その中でも、SystemVIPCシリーズの関数は、SharedMemoryのように読み書き時にオフセットや長さなどを把握する必要がなく、シリアル化とデシリアライズを行ったり来たりする必要がないため、より便利にデータを操作できます。 SharedMemory 関数は文字列パラメータのデータのみをサポートします。ただし、SystemVIPCシリーズはWindowsをサポートしていないため、win環境で使用したい場合はSharedMemoryを選択するしかありません。

PHP はデフォルトではこれらの関数をサポートしていないため、PHP を再コンパイルする必要があります。使用方法:

SystemV セマフォ、コンパイル時に –enable-sysvsem を追加

SystemV 共有メモリ、コンパイル時に –enable-sysvshm を追加

SystemV メッセージ キュー、コンパイル時に –enable-sysvmsg を追加

SharedMemory、コンパイル時に –enable-shmop を追加

まず SharedMemory の例を書きます:

<?php
$key = ftok(__FILE__, &#39;i&#39;);
$size = 100;
$shm_h = @shmop_open($key, &#39;c&#39;, 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 "there is no data";
        $data = "imdonkey";
        //就算数据是文本,write时也要序列化
        $write_size = shmop_write($shm_h, serialize($data), 0);
        if($write_size === false) echo "shmop write failed!";
}
//如果有,显示出来,之后删掉
else {
        echo "shared memory data: ";
        print_r($data);
        shmop_delete($shm_h);
}
shmop_close($shm_h);
?>
ログイン後にコピー

System V shm の別の例を書きます:

<?php
$shm_key = ftok(__FILE__, &#39;i&#39;);
$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 = "imdonkey";
	echo "there is no data, insert $data.
";
	shm_put_var($shm_h, $var_key, $data);
} else {
	echo "find data: $data
";
	shm_remove_var($shm_h, $var_key);
}
shm_detach($shm_h);
?>
ログイン後にコピー

ご覧のとおり、sysV はデータごとに対応する var_key を設定するため、shmop のように別の共有メモリ領域を適用する必要がなく、複数のデータを同じメモリ領域に保存でき、シリアル化の干渉も排除されます (ただし、データは最終的にシリアル化された形式で保存されるため、開発者が手動で実装する必要はありません)。

この例は単純ですが、shm_attach または shmop_open のいずれであっても、要求されるメモリのサイズは、後続のデータの長さを満たしている必要があります。シリアル化後のデータ自体と、PHP の少量のヘッダー情報が追加されます。公式 PHP ドキュメントの誰かが、適用されるメモリ サイズを計算する式を提案しました。この式により、要求されたメモリが指定されたデータを格納するのに十分であることが保証されます。式は次のとおりです。

<?php
//当shm_attach第一次被调用时,php向共享内存写入一个header
$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;
?>
ログイン後にコピー

この式がすべての状況に当てはまるかどうかはあえて言えませんので、プログラム側で共有メモリに置くデータ構造を設計し、データサイズが一定の範囲内になるようにするのが良いと思います。

また、共有メモリの無駄を防ぐため、データが不要になった場合には、対応するremoveメソッドを速やかに呼び出してリソースを解放します。

共有メモリを紹介した後、メッセージ キュー (これも System V IPC 関数グループに含まれます) について触れてみましょう。メッセージ キューは別のタイプの共有メモリとみなされているようですが、データの保存方法は多少異なります。簡単に言うと、各キーはキューに対応しており、各キューには複数のデータを格納できます。データは先入れ先出しの原則に従って操作されます。 PHP ドキュメントの例は、各関数のアプリケーションの優れた入門書です。

<?php
if (sizeof($argv) < 2) {
	echo "Usage: $argv[0] stat|send|receive|remove msgType MSG [msg] 

";
	echo "   EX: $argv[0] send 1 "This is no 1" 
";
	echo "       $argv[0] receive ID 
";
	echo "       $argv[0] stat 
";
	echo "       $argv[0] remove 
";
	exit;
}

$MSGKey = "123456";
$seg = msg_get_queue($MSGKey);

switch ($argv[1]) {
	case "send" :
		msg_send($seg, $argv[2], $argv[3]);
		echo "msg_send done...
";
		break;

	case "receive" :
		$stat = msg_stat_queue($seg);
		echo &#39;Messages in the queue: &#39; . $stat[&#39;msg_qnum&#39;] . "
";
		if ($stat[&#39;msg_qnum&#39;] > 0) {
			msg_receive($seg, $argv[2], $msgtype, 1024, $data, true, MSG_IPC_NOWAIT);
			var_dump($msgtype);
			var_dump($data);
			echo "
";
		} else {
			echo "No Msg...
";
		}
		break;

	case "stat" :
		print_r(msg_stat_queue($seg));
		break;

	case "remove" :
		msg_remove_queue($seg);
		break;
}
?>
ログイン後にコピー

メッセージ キュー内のデータにもサイズ制約が適用されます。具体的な制約範囲は、msg_stat_queue の msg_qbytes で確認できます。このコードの唯一の小さな変更は、メッセージを受信するときに MSG_IPC_NOWAIT が指定されていることです。それ以外の場合、ターゲット キューにデータがない場合、デフォルトで永久に待機します。

一般に、共有メモリまたはメッセージ キューが使用され、これにはマルチスレッド/プロセス、または言語間のデータ転送が含まれます。プロセス間で共有されるphpスクリプト・データであれば、気をつければ問題ありません。クロスランゲージが必要な場合は、あらゆる種類の奇妙な問題に遭遇する可能性があります(笑)、私はまだ試していませんが、インターネット上で他の人が投稿した苦い投稿を見ました。将来的にはチャンスがあります。

共有メモリ、セマフォ、メッセージ キューをデバッグする場合、ipcs や ipcrm コマンドなどの Linux システム コマンドと連携して、セマフォやメッセージ キューのデータ ストレージとリソース割り当てを監視できます。

PHP を使用して Linux メッセージ キューを操作し、プロセス間通信を完了します

私たちが開発するシステムをマルチプロセス モードで実行する必要がある場合、プロセス間通信が重要なリンクになります。メッセージキュー(messagequeue)は、Linuxシステムにおけるプロセス間通信の方式です。

Linux システムにおけるプロセス通信の概念と実装については、http://www.ibm.com/developerworks/cn/linux/l-ipc/

を参照してください。

Linux システムにおけるメッセージキューの概念と実装については、http://www.ibm.com/developerworks/cn/linux/l-ipc/part4/

を参照してください。

PHP の sysvmsg モジュールは、Linux システムでサポートされる SystemVIPC の SystemV メッセージ キュー関数ファミリーをカプセル化したものです。プロセス間通信には sysvmsg モジュールが提供する関数を使用する必要があります。まずサンプルコードを見てみましょう_1:

<?php
$message_queue_key = ftok(__FILE__, &#39;a&#39;);

$message_queue = msg_get_queue($message_queue_key, 0666);

var_dump($message_queue);

$message_queue_status = msg_stat_queue($message_queue);

print_r($message_queue_status);

//向消息队列中写

msg_send($message_queue, 1, "Hello,World!");

$message_queue_status = msg_stat_queue($message_queue);

print_r($message_queue_status);

//从消息队列中读

msg_receive($message_queue, 0, $message_type, 1024, $message, true, MSG_IPC_NOWAIT);

print_r($message . "
");

msg_remove_queue($message_queue);
?>
ログイン後にコピー

このコードを実行した結果は次のとおりです:

resource(4) of type (sysvmsg queue)
Array
(
    [msg_perm.uid] => 1000 
    [msg_perm.gid] => 1000
    [msg_perm.mode] => 438
    [msg_stime] => 0
    [msg_rtime] => 0
    [msg_ctime] => 1279849495
    [msg_qnum] => 0
    [msg_qbytes] => 16384
    [msg_lspid] => 0
    [msg_lrpid] => 0
)
Array
( 
    [msg_perm.uid] => 1000 
    [msg_perm.gid] => 1000 
    [msg_perm.mode] => 438
    [msg_stime] => 1279849495
    [msg_rtime] => 0
    [msg_ctime] => 1279849495 
    [msg_qnum] => 1
    [msg_qbytes] => 16384
    [msg_lspid] => 2184
    [msg_lrpid] => 0
)
Hello,World!
ログイン後にコピー

「Hello, World!」という文字列がメッセージ キューから正常に読み取られたことがわかります

サンプルコードの主な関数は以下のとおりです。

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用于销毁一个队列。

  示例代码_1只是展示了PHP操作消息队列函数的应用。下面的代码具体描述了进程间通信的场景

<?php
$message_queue_key = ftok(__FILE__, &#39;a&#39;);
$message_queue = msg_get_queue($message_queue_key, 0666);
$pids = array ();
for ($i = 0; $i < 5; $i++) {
	//创建子进程
	$pids[$i] = pcntl_fork();
	if ($pids[$i]) {
		echo "No.$i child process was created, the pid is $pids[$i]
";
	}elseif ($pids[$i] == 0) {
		$pid = posix_getpid();
		echo "process.$pid is writing now
";
		msg_send($message_queue, 1, "this is process.$pid&#39;s data
");
		posix_kill($pid, SIGTERM);
	}
}
do {
	msg_receive($message_queue, 0, $message_type, 1024, $message, true, MSG_IPC_NOWAIT);
	echo $message;
	//需要判断队列是否为空,如果为空就退出
	//break;
} while (true)
?>
ログイン後にコピー

运行结果为:
No.0 child process was created, the pid is 5249
No.1 child process was created, the pid is 5250
No.2 child process was created, the pid is 5251
No.3 child process was created, the pid is 5252
No.4 child process was created, the pid is 5253
process.5251 is writing now
this is process.5251's data
process.5253 is writing now
process.5252 is writing now
process.5250 is writing now
this is process.5253's data
this is process.5252's data
this is process.5250's data
process.5249 is writing now
this is process.5249's data
这段程序每次的运行结果都会不同,这正说明了多进程的异步性。从结果也能看出消息队列FIFO特性。


関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート