PHP プログラミングのロック
最近、オープンソースの書籍「Understanding Linux Processes」 (リンク) を読みました。この本では、Linux のプロセスの概念について説明し、ロックとプロセス間通信 (IPC) についていくつかの概要を説明します。ただし、この本の記述言語は golang であり、日常生活ではほとんど使用されないため、その概念に対応するインターフェイスを PHP で見つけたかっただけです。
正式名称はアドバイザリーファイルロックで、本書でも言及されています。 このタイプのロックは比較的一般的です。たとえば、mysql と php-fpm が開始された後、プロセス ID を記録する pid ファイルが作成されます。このファイルはファイル ロックです。
このロックにより、プロセスが繰り返し実行されるのを防ぐことができます。たとえば、crontab を使用する場合、1 つのタスクは 1 分ごとに実行されるように制限されますが、プロセスがロックされていない場合、このプロセスは 1 分を超えて実行される可能性があります。競合を解決するために使用すると、2 つのプロセスが一緒になるため、実行に問題が発生します。
PID ファイル ロックを使用するもう 1 つの利点は、プロセスが停止信号または再起動信号をそれ自体に送信するのに便利であることです。たとえば、php-fpm を再起動するコマンドは
kill -USR2 `cat /usr/local/php/var/run/php-fpm.pid`
で、pid ファイルに記録されているプロセスに USR2 シグナルを送信します。このシグナルはプロセス通信に属し、別の章で説明します。
php のインターフェースは flock であり、ドキュメントは比較的詳細です。まず定義を見てみましょう。 bool flock ( resource $handle , int $operation [, int &$wouldblock ] )。
$handle はファイル システム ポインターです。通常は fopen() によって使用されます。 作成されたリソース。これは、flock を使用するにはファイルを開く必要があることを意味します。
$operation は操作の種類です。
&$wouldblock ロックがブロックされている場合、この変数は 1 に設定されます。
この関数はデフォルトで設定されていることに注意してください。非ブロッキングにしたい場合は、ビットマスク LOCK_NB を操作に追加します。
$pid_file = "/tmp/process.pid";$pid = posix_getpid();$fp = fopen($pid_file, 'w+');if(flock($fp, LOCK_EX | LOCK_NB)){ echo "got the lock \n"; ftruncate($fp, 0); // truncate file fwrite($fp, $pid); fflush($fp); // flush output before releasing the lock sleep(300); // long running process flock($fp, LOCK_UN); // 释放锁定} else { echo "Cannot get pid lock. The process is already up \n";}fclose($fp);
それを process.php として保存し、php process.php & を実行してから、php process.php を再度実行すると、エラー メッセージが表示されます。 flock には、共有ロック LOCK_SH もあります。
ミューテックスは、相互排他を意味する複合語です。 pecl を使用して同期モジュール、pecl install sync をインストールします。 ドキュメント内の SyncMutex には、lock と lock の 2 つのメソッドしかありません。コードのテストに直接進みましょう。 IDE で書いたわけではないので、cs は非常に見苦しいですが、無視してください。
$mutex = new SyncMutex("UniqueName");for($i=0; $i<2; $i++){ $pid = pcntl_fork(); if($pid <0){ die("fork failed"); }elseif ($pid>0){ echo "parent process \n"; }else{ echo "child process {$i} is born. \n"; obtainLock($mutex, $i); }}while (pcntl_waitpid(0, $status) != -1) { $status = pcntl_wexitstatus($status); echo "Child $status completed\n"; }function obtainLock ($mutex, $i){ echo "process {$i} is getting the mutex \n"; $res = $mutex->lock(200); sleep(1); if (!$res){ echo "process {$i} unable to lock mutex. \n"; }else{ echo "process {$i} successfully got the mutex \n"; $mutex->unlock(); } exit();}
mutex.php として保存し、php mutex.php を実行すると、出力は次のようになります。
parent process parent process child process 1 is born. process 1 is getting the mutex child process 0 is born. process 0 is getting the mutex process 1 successfully got the mutex Child 0 completedprocess 0 unable to lock mutex. Child 0 completed
ここで、サブプロセス 0 と 1 は必ずしも前にあるわけではありません。しかし、ロックを取得できない人が常に存在します。ここでの SyncMutex::lock(int $millisecond) のパラメータはミリ秒で、ブロック期間を表し、-1 は無限ブロックを意味します。
SyncReaderWriter のメソッドも同様で、readlock、readunlock、writelock、writeunlock がペアで表示されるだけで、テスト コードは記述されていません。 Mutex コードとの一貫性を保つには、ロックを置き換えるだけです。
の Event は golang の Cond に似ており、wait() ブロック、fire() は Event によってブロックされたプロセスを起動します。 Cond を紹介する良い記事があります。Cond がロックの固定的な使用法であることがわかります。 SyncEvent についても同様です。
php ドキュメントの例では、fire() メソッドが Web アプリケーションで使用されているようです。
上記のテスト コード
for($i=0; $i<3; $i++){ $pid = pcntl_fork(); if($pid <0){ die("fork failed"); }elseif ($pid>0){ //echo "parent process \n"; }else{ echo "child process {$i} is born. \n"; switch ($i) { case 0: wait(); break; case 1: wait(); break; case 2: sleep(1); fire(); break; } }}while (pcntl_waitpid(0, $status) != -1) { $status = pcntl_wexitstatus($status); echo "Child $status completed\n"; }function wait(){ $event = new SyncEvent("UniqueName"); echo "before waiting. \n"; $event->wait(); echo "after waiting. \n"; exit();}function fire(){ $event = new SyncEvent("UniqueName"); $event->fire(); exit();}
ここでは 1 つの fire() が意図的に書かれているため、プログラムはブロックされます。これは、fire() が一度に 1 つのプロセスのみを起動することを証明しています。
Mutex、Cond、Pool も見たようですが、見る時間がありませんでした。読み終わったら追加します。
SyncSemaphore ドキュメントは、SyncSemaphore と Mutex の違いは、セマフォが一度に複数のプロセス (またはスレッド) で使用できることであることを示しています。 time ) が取得され、Mutex は一度に 1 つずつしか取得できません。そのため、SyncSemaphore のコンストラクターには、セマフォを何プロセスで取得できるかを指定するパラメーターがあります。
public SyncSemaphore::__construct ([ string $name [, integer $initialval [, bool $autounlock ]]] ) は、この $initialval (初期値) です
$lock = new SyncSemaphore("UniqueName", 2);for($i=0; $i<2; $i++){ $pid = pcntl_fork(); if($pid <0){ die("fork failed"); }elseif ($pid>0){ echo "parent process \n"; }else{ echo "child process {$i} is born. \n"; obtainLock($lock, $i); }}while (pcntl_waitpid(0, $status) != -1) { $status = pcntl_wexitstatus($status); echo "Child $status completed\n"; }function obtainLock ($lock, $i){ echo "process {$i} is getting the lock \n"; $res = $lock->lock(200); sleep(1); if (!$res){ echo "process {$i} unable to lock lock. \n"; }else{ echo "process {$i} successfully got the lock \n"; $lock->unlock(); } exit();}
この時点では、両方のプロセスがロックを取得します。
sem_get はセマフォを作成します
sem_remove はセマフォを削除します (通常は使用されません)
sem_acquire はセマフォを要求します。
sem_release はセマフォを解放します。 sem_acquire とペアで使用されます。
$key = ftok('/tmp', 'c');$sem = sem_get($key);for($i=0; $i<2; $i++){ $pid = pcntl_fork(); if($pid <0){ die("fork failed"); }elseif ($pid>0){ //echo "parent process \n"; }else{ echo "child process {$i} is born. \n"; obtainLock($sem, $i); }}while (pcntl_waitpid(0, $status) != -1) { $status = pcntl_wexitstatus($status); echo "Child $status completed\n"; }sem_remove($sem); // finally remove the semfunction obtainLock ($sem, $i){ echo "process {$i} is getting the sem \n"; $res = sem_acquire($sem, true); sleep(1); if (!$res){ echo "process {$i} unable to get sem. \n"; }else{ echo "process {$i} successfully got the sem \n"; sem_release($sem); } exit();}
ここには問題があります。sem_acquire() の 2 番目のパラメーター $nowait はデフォルトで false になり、ブロックされます。ロックの取得が失敗した場合、後続の sem_release は警告を報告します。PHP 警告: sem_release(): SysV semaphore 4 (key 0x63000081) is not currently activate in /home/jason/sysvsem.php on line 33,ロックを取得したときに解放操作を実行する必要があります。前の例では、ロックなしで解放が実行された場合、エラーは報告されません。もちろん、ロックを解除する前に確実にロックを取得できるように、ペアで表示するのが最善です。
さらに、ftok メソッドのパラメータについて説明する必要があります。最初のパラメータは、既存のアクセス可能なファイルである必要があります。通常、プロジェクト内のファイルが使用され、2 番目のパラメータは単一文字です。弦。 int を返します。
出力は
parent process parent process child process 1 is born. process 1 is getting the mutex child process 0 is born. process 0 is getting the mutex process 1 successfully got the mutex Child 0 completedprocess 0 unable to lock mutex. Child 0 completed
最後に、記事に間違いがある場合は、専門家が指摘して新人の改善に役立つことを願っています。