PHP だけで HUP シグナルを受けてリスタートする

例えば PHP でバックグラウンドな処理のために常駐するデーモン的なもの作ったとき、アプリを更新したときはプロセスをリスタートする必要があります。

普通は systemd とかでデーモンにして systemctl restart ore-no-app とかで良いのだけれども、あえて PHP 単体で HUP シグナルでリスタート出来るようにする方法。

<?php
$term = false;
$restart = false;

foreach ([SIGHUP, SIGINT, SIGTERM] as $sig) {
    pcntl_signal($sig,function ($signo) {
        global $term;
        global $restart;
        $term = true;
        if ($signo === SIGHUP) {
            $restart = true;
        }
    });
}

$pid = getmypid();
echo "\nstart [$pid]\n";

// $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

while ($term == false) {
    echo ".";
    sleep(1);
    pcntl_signal_dispatch();
}

if ($restart) {
    echo "\nrestart [$pid]\n";
    $exe = readlink("/proc/$pid/exe");
    $cmdline = file_get_contents("/proc/$pid/cmdline");
    $args = explode("\0", rtrim($cmdline, "\0"));
    pcntl_exec($exe, array_splice($args, 1));
} else {
    echo "\nstop [$pid]\n";
}

このスクリプトを実行すると、

php sample.php

次のように出力されます。

start [6394]
...............

おもむろにスクリプトを修正しても、

-    echo ".";
+    echo "x";

それだけでは何も変わりません。

start [6394]
....................

HUP をぶっ刺すと、

kill -HUP 6394

反映される。

start [6394]
.........................
restart [6394]

start [6394]
xxxxxxxxxxxxxx

注意

要するに exec で自分自分を再実行しています。exec で pid を変えずにプロセスの置き換えができる POSIX ならではの方法。

ただし、元のプロセスからファイルディスクリプタを引き継いでしまうので、なにかしらのファイルディスクリプタを開いたままで exec するとリークします。

例えば↑のスクリプトで下記のようにソケットの作成をコメントインすると・・・

-// $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
+$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

HUP でリスタートするたびにファイルディスクリプタが増えていきます。

さいごに

たぶん役に立たない。

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.