PHP 检查进程 ID

发布于 2024-08-10 10:56:55 字数 1328 浏览 4 评论 0原文

这是我想知道了一段时间并决定询问的事情。

我们有函数 getmypid() 它将返回当前脚本进程 ID。 php 中有某种函数,例如

checkifpidexists() 吗?我的意思是内置的而不是某些批处理脚本解决方案。

有没有办法改变脚本的pid?

一些说明:

我想检查 pid 是否存在,以查看脚本是否已经在运行,这样它就不会再次运行,如果你愿意的话,可以使用假 cron 作业。

我想更改 pid 的原因是这样我可以将脚本 pid 设置为非常高的值,例如 60000 并对该值进行硬编码,以便该脚本只能在该 pid 上运行,因此只有 1 个实例可以运行

编辑----

为了帮助其他人解决这个问题,我创建了这个类:

class instance {

    private $lock_file = '';
    private $is_running = false;

    public function __construct($id = __FILE__) {

        $id = md5($id);

        $this->lock_file = sys_get_temp_dir() . $id;

        if (file_exists($this->lock_file)) {
            $this->is_running = true;
        } else {
            $file = fopen($this->lock_file, 'w');
            fclose($file);
        }
    }

    public function __destruct() {
        if (file_exists($this->lock_file) && !$this->is_running) {
            unlink($this->lock_file);
        }
    }

    public function is_running() {
        return $this->is_running;
    }

}

并且您可以像这样使用它:

$instance = new instance('abcd'); // the argument is optional as it defaults to __FILE__

if ($instance->is_running()) {
    echo 'file already running';    
} else {
    echo 'file not running';
}

This is something i have wondered for a while and decided to ask about it.

We have the function getmypid() which will return the current scripts process id. Is there some kind of function such as

checkifpidexists() in php? I mean a inbuilt one and not some batch script solution.

And is there a way to change a scripts pid?

Some clarification:

I want to check if a pid exists to see if the script is already running so it dont run again, faux cron job if you will.

The reason i wanted to change the pid is so i can set the script pid to something really high such as 60000 and hard code that value so this script can only run on that pid so only 1 instance of it would run

EDIT----

To help anyone else with this proplem, i have created this class:

class instance {

    private $lock_file = '';
    private $is_running = false;

    public function __construct($id = __FILE__) {

        $id = md5($id);

        $this->lock_file = sys_get_temp_dir() . $id;

        if (file_exists($this->lock_file)) {
            $this->is_running = true;
        } else {
            $file = fopen($this->lock_file, 'w');
            fclose($file);
        }
    }

    public function __destruct() {
        if (file_exists($this->lock_file) && !$this->is_running) {
            unlink($this->lock_file);
        }
    }

    public function is_running() {
        return $this->is_running;
    }

}

and you use it like so:

$instance = new instance('abcd'); // the argument is optional as it defaults to __FILE__

if ($instance->is_running()) {
    echo 'file already running';    
} else {
    echo 'file not running';
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

弃爱 2024-08-17 10:56:56

在 Linux 中,您可以查看 /proc。

return file_exists( "/proc/$pid" );

在 Windows 中,您可以 shell_exec() tasklist.exe,这会找到匹配的进程 id:

$processes = explode( "\n", shell_exec( "tasklist.exe" ));
foreach( $processes as $process )
{
     if( strpos( "Image Name", $process ) === 0
       || strpos( "===", $process ) === 0 )
          continue;
     $matches = false;
     preg_match( "/(.*?)\s+(\d+).*$/", $process, $matches );
     $pid = $matches[ 2 ];
}

我相信您想要做的是维护一个 PID 文件。在我编写的守护进程中,它们检查配置文件,查找 pid 文件的实例,从 pid 文件中获取 pid,检查 /proc/$pid 是否存在,如果不存在,则删除 pid文件。

   if( file_exists("/tmp/daemon.pid"))
   {
       $pid = file_get_contents( "/tmp/daemon.pid" );
       if( file_exists( "/proc/$pid" ))
       {
           error_log( "found a running instance, exiting.");
           exit(1);
       }
       else
       {
           error_log( "previous process exited without cleaning pidfile, removing" );
           unlink( "/tmp/daemon.pid" );
       }
   }
   $h = fopen("/tmp/daemon.pid", 'w');
   if( $h ) fwrite( $h, getmypid() );
   fclose( $h );

进程 ID 由操作系统授予,不能保留进程 ID。您将编写守护程序来尊重 pid 文件。

In linux, you would look at /proc.

return file_exists( "/proc/$pid" );

In Windows you could shell_exec() tasklist.exe, and that would find a matching process id:

$processes = explode( "\n", shell_exec( "tasklist.exe" ));
foreach( $processes as $process )
{
     if( strpos( "Image Name", $process ) === 0
       || strpos( "===", $process ) === 0 )
          continue;
     $matches = false;
     preg_match( "/(.*?)\s+(\d+).*$/", $process, $matches );
     $pid = $matches[ 2 ];
}

I believe what you want to do is maintain a PID file. In the daemons I've written, they check a config file, look for an instance of a pid file, get the pid out of the pid file, check to see if /proc/$pid exists, and if not, delete the pid file.

   if( file_exists("/tmp/daemon.pid"))
   {
       $pid = file_get_contents( "/tmp/daemon.pid" );
       if( file_exists( "/proc/$pid" ))
       {
           error_log( "found a running instance, exiting.");
           exit(1);
       }
       else
       {
           error_log( "previous process exited without cleaning pidfile, removing" );
           unlink( "/tmp/daemon.pid" );
       }
   }
   $h = fopen("/tmp/daemon.pid", 'w');
   if( $h ) fwrite( $h, getmypid() );
   fclose( $h );

Process IDs are granted by the OS and one cannot reserve a process id. You would write your daemon to respect the pid file.

我很坚强 2024-08-17 10:56:56

实现此目的的更好方法是使用 pid 或锁定文件。只需检查 pid 文件是否存在,根据需要创建它,并用您正在运行的 pid 填充它。

<?
  class pidfile {
    private $_file;
    private $_running;

    public function __construct($dir, $name) {
      $this->_file = "$dir/$name.pid";

      if (file_exists($this->_file)) {
        $pid = trim(file_get_contents($this->_file));
        if (posix_kill($pid, 0)) {
          $this->_running = true;
        }
      }

      if (! $this->_running) {
        $pid = getmypid();
        file_put_contents($this->_file, $pid);
      }
    }

    public function __destruct() {
      if ((! $this->_running) && file_exists($this->_file)) {
        unlink($this->_file);
      }
    }

    public function is_already_running() {
      return $this->_running;
    }
  }
?>

并按如下方式使用它:

<?
  $pidfile = new pidfile('/tmp', 'myscript');
  if($pidfile->is_already_running()) {
    echo "Already running.\n";
    exit;
  } else {
    echo "Started...\n";
  }
?>

这里没有太多错误检查,但快速运行表明这在我的系统上有效。

A better way to accomplish this would be to use a pid or a lock file. Simply check for the existence of the pid file, create it as necessary, and populate it with your running pid.

<?
  class pidfile {
    private $_file;
    private $_running;

    public function __construct($dir, $name) {
      $this->_file = "$dir/$name.pid";

      if (file_exists($this->_file)) {
        $pid = trim(file_get_contents($this->_file));
        if (posix_kill($pid, 0)) {
          $this->_running = true;
        }
      }

      if (! $this->_running) {
        $pid = getmypid();
        file_put_contents($this->_file, $pid);
      }
    }

    public function __destruct() {
      if ((! $this->_running) && file_exists($this->_file)) {
        unlink($this->_file);
      }
    }

    public function is_already_running() {
      return $this->_running;
    }
  }
?>

And use it as follows:

<?
  $pidfile = new pidfile('/tmp', 'myscript');
  if($pidfile->is_already_running()) {
    echo "Already running.\n";
    exit;
  } else {
    echo "Started...\n";
  }
?>

There's not much error checking here, but a quick run shows this works on my system.

反差帅 2024-08-17 10:56:56

为了检查 Windows 机器上是否存在 PID,我使用:

function pidExists($pid)
{
    exec('TASKLIST /NH /FO "CSV" /FI "PID eq '.$pid.'"', $outputA );
    $outputB = explode( '","', $outputA[0] );
    return isset($outputB[1])?true:false;
}

请注意,如果 pid 确实不存在,则 $outputB[0] 包含一条无法找到 pid 的消息!因此,为了验证我使用第二个参数。

编辑:

要对此进行扩展,还可以使用 powershell 在后台窗口中动态生成脚本,如下所示:

    // this function builds an argument list to parse into the newly spawned script. 
    // which can be accessed through the superglobal  global $argv;
    function buildArgList( array $arguments){
     return ' '. implode(' ', $arguments) .' ';
    }

    $arguments = buildArgList(['argument1','argument2','argument3']);
    $windowstyle = 'normal'; // or you can use hidden to hide the CLI

    pclose(popen("powershell start-process -FilePath '" . PHP_BINARY . "' -ArgumentList '-f\"" . $file . " " . $arguments . "\"' -WindowStyle " . $windowstyle,"r"));   

然后您生成的脚本可以使用:cli_set_process_title
将该进程的标题设置为某个唯一的哈希值。

在生成子进程的父进程中,您可以使用以下代码通过其 windowtitle 搜索 uniquehash 在任务列表中查找该进程。

exec('TASKLIST /NH /FO "CSV" /FI "windowtitle eq ' . escapeshellarg($uniquehash) . '"', $output );

与数据库结合使用时,您基本上可以构建一个workermanager
不同 php 脚本之间的通信。

For checking if a PID exist on a windows machine i use:

function pidExists($pid)
{
    exec('TASKLIST /NH /FO "CSV" /FI "PID eq '.$pid.'"', $outputA );
    $outputB = explode( '","', $outputA[0] );
    return isset($outputB[1])?true:false;
}

Note that $outputB[0] contains a messages that pid can't be found, if the pid indeed doesn't exists! So to validate i use the second argument.

EDIT:

To expand on this, its also possible to dynamically spawn scripts within windows in the background using powershell like so:

    // this function builds an argument list to parse into the newly spawned script. 
    // which can be accessed through the superglobal  global $argv;
    function buildArgList( array $arguments){
     return ' '. implode(' ', $arguments) .' ';
    }

    $arguments = buildArgList(['argument1','argument2','argument3']);
    $windowstyle = 'normal'; // or you can use hidden to hide the CLI

    pclose(popen("powershell start-process -FilePath '" . PHP_BINARY . "' -ArgumentList '-f\"" . $file . " " . $arguments . "\"' -WindowStyle " . $windowstyle,"r"));   

The script you spawn can then use: cli_set_process_title
to set that process's title to some unique hash.

within the parent that spawned the child process you can use the following code to find that process within the tasklist using its windowtitle searching for the uniquehash.

exec('TASKLIST /NH /FO "CSV" /FI "windowtitle eq ' . escapeshellarg($uniquehash) . '"', $output );

When combined with a database you can essentially build a workermanager
communicating between different php scripts.

孤凫 2024-08-17 10:56:56

不,您不能更改任何进程 pid。它由内核分配,是内核数据结构的一部分

No you cannot change any processes pid. It is assigned by the kernel and is part of the kernel's data structures

枯寂 2024-08-17 10:56:56

正如其他人所说,您无法更改进程 ID - 它是由操作系统内核分配和完全管理的。此外,您还没有说这是基于命令行还是基于网络服务器:如果是后者,您甚至可能无法获得脚本的 pid。

getmypid() 手册页包含一些“乐观”锁定的示例。我使用 optimisitc 这个词是因为 PHP 永远不会接近像 ASP.NET Web 应用程序那样的地方,在这种应用程序中,您拥有一个带有共享/静态类的真正线程环境,因此可以使用/滥用 Singleton。基本上,您可以选择:

  • 触摸文件系统上某处的“锁定文件”。然后,您的脚本检查该文件是否存在:如果存在,则终止,否则,触摸该文件并继续处理
  • 设置基于数据库的标志来表示脚本正在运行。如上所述,但使用数据库表/字段将脚本标记为正在运行。

这两者都依赖于脚本正确终止(因为最后一步是删除锁定文件/数据库标志)。如果脚本因任何原因(或机器本身)崩溃,您可以通过手动清理过程来删除该标志。对此没有简单的解决方案,但探索的一个途径是查看对锁进行日期标记,并采用任意的“如果早于 X,则上次运行必定崩溃”的方法。

As others have said, you cannot change the process id - it is assigned and entirely manged by the kernel of the OS. Additionally, you have not said if this is command-line or web-server based: if it's the latter you may not even be getting the pid of your script.

The manual page for getmypid() contains some examples of "optimistic" locking. I use the word optimisitc as PHP is never ever going to approach the likes of an asp.net web application where you have a true threaded environment with shared/static classes and thus Singleton's to use/abuse. Basically you have the option of:

  • Touching a "lock file" on the file-system somewhere. Your script then checks if that file exists: if it does, terminate, otherwise, touch that file and carry on processing
  • Setting a database based flag to say the script is running. As above, but use a db table/field to mark a script as running.

Both of these rely on the script terminating correctly (as the last step would be to remove the lock file/db flag). If a script crashes for any reason (or the machine itself), you can be left with a manual tidy-up process to remove the flag. There is no easy solution for this, but one avenue to explore would be to then look at date-stamping the lock, with an arbitary "if older than X, the last run must have crashed" approach.

奶气 2024-08-17 10:56:56

还不要忘记,您可以通过反引号 (`) 访问 shell 命令,这将使您可以访问用于处理 pid 的标准 *nix 工具。

来源: http://www.php.net/manual/en/语言.operators.execution.php

Don't forget also that you can access shell commands via backticks (`), which would give you access to the standard *nix tools for working with pids.

source: http://www.php.net/manual/en/language.operators.execution.php

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文