目前,我试图阻止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
我发现有两个实例正在运行!不是三个或更多,而不是一个。我杀死了其中一个实例。几天后,再次有两个实例。
代码有什么问题?还有其他选择只限制onlytask.php的一个实例正在运行吗?
P.S。我的/tmp/
文件夹未清除。 ls -al /tmp/*.lock
显示锁定文件是在第一天创建的:
-rw-r--r-- 1 root root 0 Dec 4 04:03 onlyme.lock
答案 0 :(得分:10)
打开锁定文件时应使用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);
}
的注释
' x ' - 仅限写作创建和打开;将文件指针放在 文件的开头。如果文件已存在,则调用fopen() 将返回FALSE并生成级别错误 E_WARNING。如果该文件不存在,请尝试创建它。这是 相当于为底层指定O_EXCL | O_CREAT标志 open(2)系统调用。
以下是来自man page的O_EXCL
解释:
O_EXCL - 如果设置了O_CREAT和O_EXCL,则open()如果文件失败 存在。检查文件是否存在以及创建 文件如果不存在相对于其他文件应该是原子的 执行open()的线程在同一个文件名中命名相同的文件名 设置了O_EXCL和O_CREAT的目录。如果设置了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
。
答案 1 :(得分:6)
现在我检查进程是否由ps
运行并通过bash
脚本扭曲php脚本:
#!/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
脚本。
答案 2 :(得分: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”结束检查所有正在运行的应用程序,检查此文件是否在列表中。
答案 3 :(得分: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";
}
答案 4 :(得分:0)
您可以像某些人建议的那样使用锁定文件,但您真正需要的是PHP Semaphore函数。这些类似于文件锁,但专门针对您正在进行的操作而设计,限制了对共享资源的访问。
答案 5 :(得分:0)
永远不要对锁文件或重命名等其他功能使用取消链接。它打破了Linux上的LOCK_EX。例如,在取消链接或重命名锁定文件后,任何其他脚本始终从flock()获得。
检测上一个有效结束的最佳方法 - 在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;
}