PHP7におけるデーモン実装の詳細説明

little bottle
リリース: 2023-04-06 10:58:01
転載
3374 人が閲覧しました

この記事では主に PHP7 を使用してデーモン デーモン プロセスを実装する方法について説明します。一定の参考価値があるので、興味のある方は学習してください。

マルチタスク コンピュータ オペレーティング システムでは、デーモン プロセス はバックグラウンドで実行されるコンピュータ プログラムです。このようなプログラムはプロセスとして初期化されます。デーモン プログラムの名前は通常、文字「d」で終わります。たとえば、syslogd はシステム ログを管理するデーモンを指します。

デーモン プログラムは、常時実行されているサーバー プログラムであり、デーモン プロセスとも呼ばれます。通常、デーモン プログラムはシステムのバックグラウンドで実行されます。制御端末はなく、フォアグラウンドとは対話しません。通常、デーモン プログラムはシステム サービスとして使用されます。デーモンは、通常、システムの起動後に実行され、システムのシャットダウン時に終了する、長時間実行されるプロセスです。一般に、デーモン プログラムは制御端末を持たず、フォアグラウンドでユーザーと対話できないため、バックグラウンドで実行されます。デーモン プログラムは通常、クライアント プログラムが通信するのを待つサービス プログラムとして使用されます。実行中のデーモン プログラムをデーモン プロセスとも呼びます。

通常、デーモン プロセスには既存の親プロセス (つまり、PPID=1) がなく、UNIX システム プロセス階層の init の直下に配置されます。デーモン プロセスは通常、次の方法で自分自身をデーモンにします。 子プロセスで fork を実行し、その親プロセスをすぐに終了して、子プロセスを ## で使用できるようにします。 #init で実行します。この方法は、「シェル処理」と呼ばれることがよくあります。

システムは通常、起動時にデーモン プロセスを開始します。デーモンは、特定のタスクを通じてネットワーク要求、ハードウェアアクティビティ、または他のアプリケーションからのその他の要求に応答するためのサポートを提供します。デーモンは、ハードウェア (一部の Linux システムの devfsd など) を構成したり、スケジュールされたタスク (cron など) を実行したり、その他のタスクを実行したりすることもできます。各プロセスには親プロセスがあり、子プロセスが終了すると、親プロセスは子プロセスの終了ステータスを取得できます。

デーモン プロセスは、単にターミナルを使用せずにバックグラウンドで実行できるプロセスです。これは Linux では非常に一般的なプロセスです。たとえば、Apache や mysql などのサービスが開始された後などです。 , デーモンプロセスとしてメモリ上に常駐します。デーモンはバックグラウンドで実行されるアプリケーションであり、ユーザーが直接操作することはありません。デーモンの例としては、Cron や MySQL があります。 PHP デーモンの使用は非常に簡単で、PHP 4.1 以降のコンパイル パラメータが必要です。 #--enable-pcntl

# #バックグラウンドで実行する必要がある時間のかかるタスクがあるとします: キャッシュを予熱するためにすべての mysql のユーザー テーブル内の 2,000 万ユーザーすべてを Redis にインポートすると、このタスクはおそらくしばらく終了しないでしょう。 、システム内でデーモンとして実行する php スクリプトを記述する必要があり、終了後に自動的に起動されます。

Linux では、バックグラウンド スクリプトには 3 つの方法があります:

1. コマンド # の後に & 記号 ## を追加します。 例: php task.php &. この方法の欠点は、ターミナルが正常に閉じられたか異常に閉じられたかに関係なく、ターミナルが閉じられた場合、php プロセスが終了したときに php プロセスも閉じられることです。次に、コード内に echo や print_r などの出力テキストがある場合は、現在のターミナル ウィンドウに出力されます。

2. nohup コマンド

を使用します。たとえば、

nohup php task.php &

。デフォルトでは、echoまたは、print_r で出力したテキストが PHP コードと同じディレクトリにある nohup.out ファイルに出力されます exit コマンドや閉じるボタンなど通常の方法でターミナルを閉じた場合、プロセスは実行されません閉じられており、バックグラウンドで引き続き実行されます。ただし、ターミナルが異常な終了または終了に遭遇した場合、php プロセスもすぐに終了します。本質的に、これは安定した信頼性の高いデーモン ソリューションではありません。 ##3.

pcntl

および posix 拡張機能を介して実装されますプログラミング中に注意すべき点は次のとおりです:

  • セカンダリ pcntl_fork() および posix_setsid を介してメイン プロセスを端末から離れるようにします
  • ## を介して# pcntl_signal() 無視または処理 SIGHUP signal
  • マルチプロセス プログラムは 2 回渡す必要があります pcntl_fork() または pcntl_signal () SIGCHLD シグナルを無視して、子プロセスがゾンビ プロセスになるのを防ぎます
  • umask( )、継承されたファイル権限からの権限の影響を防止します
  • 実行中のプロセスの STDIN/STDOUT/STDERR/dev/ にリダイレクトします。 null または他のストリーム上で

##デーモンには次の特性があります:

    いいえターミナル
  • バックグラウンド実行
  • 親プロセスの PID は 1
必要です実行中のデーモンの表示 プロセスは、

ps -ax または ps -ef を介して表示できます。-x は、端末を制御しないプロセスがリストされることを意味します。

fork システム コール

fork システム コールは、親プロセスとほぼ同一のプロセスをコピーするために使用されます。プロセスが異なります。問題は、親プロセスの PID とメモリ空間が異なることです。コード ロジックに従って、親プロセスと子プロセスは同じ作業を完了することも、異なる作業を完了することもできます。子プロセスは、ファイル記述子などのリソースを親プロセスから継承します。

PHP の

pcntl 拡張機能は、PHP で新しいプロセスをフォークするために使用される pcntl_fork() 関数を実装します。

setsid システム コール

setsid システム コールは、新しいセッションを作成し、プロセス グループ ID を設定するために使用されます。ここにはいくつかの概念があります:

セッションプロセス グループ

Linux では、ユーザーのログインによりセッションが生成され、セッションには 1 つ以上のプロセス グループが含まれ、プロセス グループには複数のプロセスが含まれます。各プロセス グループにはグループ リーダー (

セッション リーダー) があり、その pid はプロセス グループのグループ ID です。プロセス リーダーがターミナルを開くと、このターミナルは制御ターミナルと呼ばれます。制御端末で例外(切断、ハードウェアエラーなど)が発生すると、プロセスグループリーダーに信号が送信されます。

バックグラウンドで実行されているプログラム (

& で終わるシェル実行命令など) も、端末が閉じられた後、つまり制御端末が切断されたときに強制終了されます。 SIGHUP シグナルが発行され、SIGHUP シグナルのデフォルトの動作はプロセスを終了することです。 。

Call setsid システム コールの後、現在のプロセスは新しいプロセス グループを作成します。現在のプロセスでターミナルが開かれていない場合は、これは、プロセス グループ内に制御端末は存在しないため、端末を閉じてプロセスを強制終了しても問題はありません。

PHP の

posix 拡張機能は、PHP グループに新しいプロセスを設定するために使用される posix_setsid() 関数を実装します。

2 番目のフォークの役割

まず、

setsid システム コールはプロセスから呼び出すことができません。グループリーダーは -1 を返します。

2 番目のフォーク操作のサンプル コードは次のとおりです:

<span style="font-size: 16px;">$pid1 = pcntl_fork();

if ($pid1 > 0) {</span><br/><span style="font-size: 16px;">    // 父进程会得到子进程号,所以这里是父进程执行的逻辑
    exit(&#39;parent process. 1&#39;."\n");
} else if ($pid1 < 0) {
    exit("Failed to fork 1\n");
}

if (-1 == posix_setsid()) {
    exit("Failed to setsid\n");
}

$pid2 = pcntl_fork();

if ($pid2 > 0) {
    exit(&#39;parent process. 2&#39;."\n");
} else if ($pid2 < 0) {
    exit("Failed to fork 2\n");
}</span>
ログイン後にコピー

pcntl_fork() 関数は子プロセスを作成します。子プロセス PID (プロセス ID) と PPID (親プロセス ID) のみが親プロセスと異なります。

戻り値

成功すると、生成された子プロセスの PID が親プロセスの実行スレッドに返されます。子プロセスで実行されます。スレッドには

0 が返されます。 が失敗すると、親プロセスのコンテキストには -1 が返されます。子プロセスは作成されず、PHP が実行されます。エラーが発生します。

假定我们在终端中执行应用程序,进程为 a,第一次 fork 会生成子进程 b,如果 fork 成功,父进程 a 退出。b 作为孤儿进程,被 init 进程托管。

此时,进程 b 处于进程组 a 中,进程 b 调用 posix_setsid 要求生成新的进程组,调用成功后当前进程组变为 b。


php fork2.php 
parent process. 1
parent process. 2
ログイン後にコピー

此时进程 b 事实上已经脱离任何的控制终端,例程:


cli_set_process_title(&#39;process_a&#39;);

$pidA = pcntl_fork();

if ($pidA > 0) {
    exit(0);
} else if ($pidA < 0) {
    exit(1);
}

cli_set_process_title(&#39;process_b&#39;);

if (-1 === posix_setsid()) {
    exit(2);
}

while(true) {
    sleep(1);
}
ログイン後にコピー

执行程序之后:  


$ php cli-title.php 
$ ps ax | grep -v grep | grep -E &#39;process_|PID&#39;
  PID TTY      STAT   TIME COMMAND
15725 ?        Ss     0:00 process_b
ログイン後にコピー

重新打开一个shell窗口,效果一样,都在呢

从 ps 的结果来看,process_b 的 TTY 已经变成了 ,即没有对应的控制终端。

代码走到这里,似乎已经完成了功能,关闭终端之后 process_b 也没有被杀死,但是为什么还要进行第二次 fork 操作呢?

StackOverflow 上的一个回答写的很好:

The second fork(2) is there to ensure that the new process is not a session leader, so it won’t be able to (accidentally) allocate a controlling terminal, since daemons are not supposed to ever have a controlling terminal.

这是为了防止实际的工作的进程主动关联或者意外关联控制终端,再次 fork 之后生成的新进程由于不是进程组组长,是不能申请关联控制终端的。

综上,二次 fork 与 setsid 的作用是生成新的进程组,防止工作进程关联控制终端。 

写一个demo测试下


<?php
// 第一次fork系统调用
$pid_A = pcntl_fork();

// 父进程 和 子进程 都会执行下面代码
if ($pid_A < 0) {
    // 错误处理: 创建子进程失败时返回-1.
    exit(&#39;A fork error &#39;);
} else if ($pid_A > 0) {
     // 父进程会得到子进程号,所以这里是父进程执行的逻辑
    exit("A parent process exit \n");
}

// B 作为孤儿进程,被 init 进程托管,此时,进程 B 处于进程组 A 中

// 子进程得到的$pid为0, 所以以下是子进程执行的逻辑,受控制终端的影响,控制终端关闭则这里也会退出

// [子进程] 控制终端未关闭前,将当前子进程提升会会话组组长,及进程组的leader
// 进程 B 调用 posix_setsid 要求生成新的进程组,调用成功后当前进程组变为 B
if (-1 == posix_setsid()) {
    exit("Failed to setsid\n");
}

// 此时进程 B 已经脱离任何的控制终端

// [子进程]  这时候在【进程组B】中,重新fork系统调用(二次fork)
$pid_B = pcntl_fork();
if ($pid_B < 0) {
    exit(&#39;B fork error &#39;);
} else if ($pid_B > 0) {
    exit("B parent process exit \n");
}

// [新子进程] 这里是新生成的进程组,不受控制终端的影响,写写自己的业务逻辑代码
for ($i = 1; $i <= 100; $i++) {
    sleep(1);
    file_put_contents(&#39;daemon.log&#39;,$i . "--" . date("Y-m-d H:i:s", time()) . "\n",FILE_APPEND);
}
ログイン後にコピー

Window 下跑回直接抛出异常


php runtime\daemon.php
PHP Fatal error:  Uncaught Error: Call to undefined function pcntl_fork() in D:\phpStudy\PHPTutorial\WWW\notes\runtime\daemon.php:13
Stack trace:
#0 {main}
  thrown in D:\phpStudy\PHPTutorial\WWW\notes\runtime\daemon.php on line 13
ログイン後にコピー

Linux 下执行,输出结果


<span style="font-size: 16px;">php daemon.php</span><br/><span style="font-size: 16px;">...
97--2018-09-07 03:50:09
98--2018-09-07 03:50:10
99--2018-09-07 03:50:11
100--2018-09-07 03:50:12</span>
ログイン後にコピー

所以,现在即使关闭了终端,改脚本任然在后台守护进程运行

相关教程:PHP视频教程

以上がPHP7におけるデーモン実装の詳細説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:cnblogs.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート