Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

fork 直前にシグナルを受けると親と子の両方が受ける例

More than 5 years have passed since last update.

タイムラインに次のようなものが流れてきまして、Ruby と同じ原因かどうかわかりませんが PHP でもそうなるなーと思ったので試しました。

PHP の pcntl_signal で登録するシグナルハンドラは、実は本当のシグナルハンドラとして実行されるわけではなく、受けたシグナルを一旦 PHP の側で(というか pcntl 拡張の中で)キューに保存して pcntl_signal_dispatch の呼び出し時に普通に PHP のコールバック関数を呼ぶような感じで呼ばれます(declare(ticks=1) していれば pcntl_signal_dispatch が無くても勝手に呼ばれます)。

なので本来であればシグナルハンドラではできないような処理も記述できるのですが、次のようなコードだと pcntl_signal_dispatch を呼んでから pcntl_fork するまでに受けたシグナルは親と子の両方で処理されます。pcntl 拡張の中で保持されているシグナルのキューごと fork されるからです。

<?php
pcntl_signal(SIGHUP, function () {
    $pid = posix_getpid();
    echo "SIGHUP [$pid]\n";
});

$pid = posix_getpid();
echo "I am [$pid]\n";

echo "ここで HUP を打つと親だけ処理する\n";
sleep(10);

pcntl_signal_dispatch();

echo "ここで HUP を打つと親と子の両方が処理する\n";
sleep(10);

$pid = pcntl_fork();

if ($pid < 0) {
    exit("!!!fork");
} else if ($pid > 0) {
    // 親
    echo "Forked child process [$pid]\n";
    for (;;) {
        pcntl_signal_dispatch();
        sleep(1);
    }
} else {
    // 子
    for (;;) {
        pcntl_signal_dispatch();
        sleep(1);
    }
}

解決方法

次のように fork の前に pcntl_sigprocmask でシグナルをブロックし、pcntl_signal_dispatch でシグナルのキューを空にしたうえで pcntl_fork し、その後 pcntl_sigprocmask でブロックを解除するといいのではないでしょうか(ブロック中に受けたシグナルはブロックを解除したときに処理されます)。

<?php
pcntl_signal(SIGHUP, function () {
    $pid = posix_getpid();
    echo "SIGHUP [$pid]\n";
});

$pid = posix_getpid();
echo "I am [$pid]\n";

echo "ここで HUP を打つと親だけ処理する\n";
sleep(10);

pcntl_sigprocmask(SIG_BLOCK, [SIGHUP]);
pcntl_signal_dispatch();

echo "ここで HUP を打ってもブロックされるのでブロックが解除されるまでペンディングされる\n";
sleep(10);

$pid = pcntl_fork();
pcntl_sigprocmask(SIG_UNBLOCK, [SIGHUP]);

if ($pid < 0) {
    exit("!!!fork");
} else if ($pid > 0) {
    // 親
    echo "Forked child process [$pid]\n";
    for (;;) {
        pcntl_signal_dispatch();
        sleep(1);
    }
} else {
    // 子
    for (;;) {
        pcntl_signal_dispatch();
        sleep(1);
    }
}
ngyuki
テック系男子。 ただのやってみた系の記事ははてなブログに、それ以外の技術系のネタは Qiita に投稿します。
https://ngyuki.jp/
headjapan
中規模~大規模の安定した基幹システム・大規模サイトの分析・要件定義・設計・開発を得意とする、総合的な開発会社です。
http://www.headjapan.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away