10

現在、onlytask.phpスクリプトが複数回実行されないようにしようとしました。

$fp = fopen("/tmp/"."onlyme.lock", "a+");
if (flock($fp, LOCK_EX | LOCK_NB)) {
  echo "task started\n";
  //
    while (true) {
      // do something lengthy
      sleep(10);
    }
  //
  flock($fp, LOCK_UN);
} else {
  echo "task already running\n";
}
fclose($fp);

上記のスクリプトを毎分実行するcronジョブがあります。

* * * * * php /usr/local/src/onlytask.php

しばらくは動作します。数日後、私がするとき:

ps auxwww | grep onlytask

2つのインスタンスが実行されていることがわかりました。3つ以上ではなく、1つでもありません。インスタンスの1つを殺しました。数日後、再び2つのインスタンスがあります。

コードの何が問題になっていますか?onlytask.phpのインスタンスを1つだけ実行するように制限する他の方法はありますか?

ps私の/tmp/フォルダはクリーンアップされていません。ls -al /tmp/*.lockロックファイルが初日に作成されたことを示します。

-rw-r--r--  1 root root    0 Dec  4 04:03 onlyme.lock
4

7 に答える 7

12

xロックファイルを開くときは、フラグを使用する必要があります。

<?php

$lock = '/tmp/myscript.lock';
$f = fopen($lock, 'x');
if ($f === false) {
  die("\nCan't acquire lock\n");
} else {
  // Do processing
  while (true) {
    echo "Working\n";
    sleep(2);
  }
  fclose($f);
  unlink($lock);
}

PHPマニュアルからの注意

' x ' - 書き込み専用に作成して開きます。ファイルポインタをファイルの先頭に置きます。ファイルが既に存在する場合、fopen() 呼び出しは FALSE を返し、レベル E_WARNING のエラーを生成して失敗します。ファイルが存在しない場合は、作成してみてください。これは、基礎となる open(2) システムコールに O_EXCL|O_CREAT フラグを指定することと同じです。

そして、ここにmanページO_EXCLからの説明があります:

O_EXCL - O_CREAT と O_EXCL が設定されている場合、ファイルが存在する場合、open() は失敗します。ファイルが存在するかどうかのチェックと、存在しない場合のファイルの作成は、O_EXCL と O_CREAT が設定された同じディレクトリで同じファイル名を指定して open() を実行する他のスレッドに関してアトミックである必要があります。O_EXCL と O_CREAT が設定され、パス名がシンボリック リンクの場合、シンボリック リンクの内容に関係なく、open() は失敗し、errno を [EEXIST] に設定します。O_EXCL が設定され、O_CREAT が設定されていない場合、結果は未定義です。

更新

より信頼性の高いアプローチ - ロックを取得し、ワーカー スクリプトを実行してロックを解放するメイン スクリプトを実行します。

<?php
// File: main.php

$lock = '/tmp/myscript.lock';
$f = fopen($lock, 'x');
if ($f === false) {
  die("\nCan't acquire lock\n");
} else {
  // Spawn worker which does processing (redirect stderr to stdout)
  $worker = './worker 2>&1';
  $output = array();
  $retval = 0;
  exec($worker, $output, $retval);
  echo "Worker exited with code: $retval\n";
  echo "Output:\n";
  echo implode("\n", $output) . "\n";
  // Cleanup the lock
  fclose($f);
  unlink($lock);
}

ここに労働者が行きます。その中で偽の致命的なエラーを発生させましょう:

#!/usr/bin/env php
<?php
// File: worker (must be executable +x)
for ($i = 0; $i < 3; $i++) {
  echo "Processing $i\n";
  if ($i == 2) {
    // Fake fatal error
    trigger_error("Oh, fatal error!", E_USER_ERROR);
  }
  sleep(1);
}

これが私が得た出力です:

galymzhan@atom:~$ php main.php 
Worker exited with code: 255
Output:
Processing 0
Processing 1
Processing 2
PHP Fatal error:  Oh, fatal error! in /home/galymzhan/worker on line 8
PHP Stack trace:
PHP   1. {main}() /home/galymzhan/worker:0
PHP   2. trigger_error() /home/galymzhan/worker:8

重要な点は、ロック ファイルが適切にクリーンアップされるmain.phpため、問題なく再実行できることです。

于 2012-12-12T09:53:02.153 に答える
9

ここで、プロセスが実行されているかどうかを確認psし、スクリプトによって php スクリプトをワープしbashます。

 #!/bin/bash

 PIDS=`ps aux | grep onlytask.php | grep -v grep`
 if [ -z "$PIDS" ]; then
     echo "Starting onlytask.php ..."
     php /usr/local/src/onlytask.php >> /var/log/onlytask.log &
 else
     echo "onlytask.php already running."
 fi

毎分bashスクリプトを実行します。cron

于 2012-12-18T06:24:27.437 に答える
1
<?php

$sLock = '/tmp/yourScript.lock';

if( file_exist($sLock) ) {
 die( 'There is a lock file' );
}

file_put_content( $sLock, 1 );

// A lot of code

unlink( $sLock );

pid を記述して追加のチェックを追加し、file_exist-statement 内でチェックすることができます。さらに安全にするには、「ps fax」で実行中のすべてのアプリケーションを取得し、このファイルがリストにあるかどうかを確認します。

于 2012-12-12T09:12:41.183 に答える
0

flock フラグではなく、ファイルの存在を使用してみてください:

$lockFile = "/tmp/"."onlyme.lock";
if (!file_exists($lockFile)) {

  touch($lockFile); 

  echo "task started\n";
  //
  // do something lengthy
  //

  unlink($lockFile); 

} else {
  echo "task already running\n";
}
于 2012-12-12T09:16:18.527 に答える
0

一部の人が示唆しているように、ロックファイルを使用できますが、実際に探しているのは PHPセマフォ関数です。これらはファイル ロックのようなものですが、共有リソースへのアクセスを制限するために特別に設計されています。

于 2012-12-28T01:06:15.947 に答える
0

ファイルのロックや、名前の変更などの他の機能に unlink を使用しないでください。Linux で LOCK_EX を壊します。たとえば、ロック ファイルのリンクを解除または名前を変更した後、他のスクリプトは常に flock() から true になります。

前の有効な終了を検出する最善の方法 - LOCK_UN が処理する前に、終了ロックの数バイトをロック ファイルに書き込みます。LOCK_EX の後、ロック ファイルと ftruncate ハンドルから数バイトを読み取ります。

重要な注意事項: すべて Linux 上の PHP 5.4.17 および Windows 7 上の 5.4.22 でテストされています。

コード例:

セマフォを設定:

$handle = fopen($lockFile, 'c+');
if (!is_resource($handle) || !flock($handle, LOCK_EX | LOCK_NB)) {
    if (is_resource($handle)) {
        fclose($handle);
    }
    $handle = false;
    echo SEMAPHORE_DENY;
    exit;
} else {
    $data = fread($handle, 2);
    if ($data !== 'OK') {
        $timePreviousEnter = fileatime($lockFile);
        echo SEMAPHORE_ALLOW_AFTER_FAIL;
    } else {
        echo SEMAPHORE_ALLOW;
    }
    fseek($handle, 0);
    ftruncate($handle, 0);
}

セマフォを離れます (シャットダウン ハンドラで呼び出したほうがよい):

if (is_resource($handle)) {
    fwrite($handle, 'OK');
    flock($handle, LOCK_UN);
    fclose($handle);
    $handle = false;
}
于 2014-01-04T20:22:21.047 に答える