この記事では主に 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 つの方法があります:
2. nohup コマンド
。デフォルトでは、echoまたは、print_r で出力したテキストが PHP コードと同じディレクトリにある nohup.out ファイルに出力されます exit コマンドや閉じるボタンなど通常の方法でターミナルを閉じた場合、プロセスは実行されません閉じられており、バックグラウンドで引き続き実行されます。ただし、ターミナルが異常な終了または終了に遭遇した場合、php プロセスもすぐに終了します。本質的に、これは安定した信頼性の高いデーモン ソリューションではありません。 ##3.
pcntl および posix 拡張機能を介して実装されますプログラミング中に注意すべき点は次のとおりです:
pcntl_fork()
および posix_setsid
を介してメイン プロセスを端末から離れるようにします 無視または処理
SIGHUP signal
または
pcntl_signal ()
SIGCHLD シグナルを無視して、子プロセスがゾンビ プロセスになるのを防ぎます
、継承されたファイル権限からの権限の影響を防止します
を
/dev/ にリダイレクトします。 null または他のストリーム上で
##デーモンには次の特性があります:
ps -ax または ps -ef
を介して表示できます。-x
は、端末を制御しないプロセスがリストされることを意味します。
PHP の
pcntl 拡張機能は、PHP で新しいプロセスをフォークするために使用される pcntl_fork()
関数を実装します。
セッション、プロセス グループ
。
セッション リーダー) があり、その pid はプロセス グループのグループ ID です。プロセス リーダーがターミナルを開くと、このターミナルは制御ターミナルと呼ばれます。制御端末で例外(切断、ハードウェアエラーなど)が発生すると、プロセスグループリーダーに信号が送信されます。
バックグラウンドで実行されているプログラム (& で終わるシェル実行命令など) も、端末が閉じられた後、つまり制御端末が切断されたときに強制終了されます。 SIGHUP
シグナルが発行され、
SIGHUP シグナルのデフォルトの動作はプロセスを終了することです。 。
Call setsid システム コールの後、現在のプロセスは新しいプロセス グループを作成します。現在のプロセスでターミナルが開かれていない場合は、これは、プロセス グループ内に制御端末は存在しないため、端末を閉じてプロセスを強制終了しても問題はありません。
PHP のposix 拡張機能は、PHP グループに新しいプロセスを設定するために使用される posix_setsid()
関数を実装します。
setsid システム コールはプロセスから呼び出すことができません。グループリーダーは -1 を返します。
<span style="font-size: 16px;">$pid1 = pcntl_fork();
if ($pid1 > 0) {</span><br/><span style="font-size: 16px;"> // 父进程会得到子进程号,所以这里是父进程执行的逻辑
exit('parent process. 1'."\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('parent process. 2'."\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 调用 此时进程 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测试下 Window 下跑回直接抛出异常 Linux 下执行,输出结果 相关教程:PHP视频教程posix_setsid
要求生成新的进程组,调用成功后当前进程组变为 b。php fork2.php
parent process. 1
parent process. 2
cli_set_process_title('process_a');
$pidA = pcntl_fork();
if ($pidA > 0) {
exit(0);
} else if ($pidA < 0) {
exit(1);
}
cli_set_process_title('process_b');
if (-1 === posix_setsid()) {
exit(2);
}
while(true) {
sleep(1);
}
$ php cli-title.php
$ ps ax | grep -v grep | grep -E 'process_|PID'
PID TTY STAT TIME COMMAND
15725 ? Ss 0:00 process_b
?
,即没有对应的控制终端。<?php
// 第一次fork系统调用
$pid_A = pcntl_fork();
// 父进程 和 子进程 都会执行下面代码
if ($pid_A < 0) {
// 错误处理: 创建子进程失败时返回-1.
exit('A fork error ');
} 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('B fork error ');
} else if ($pid_B > 0) {
exit("B parent process exit \n");
}
// [新子进程] 这里是新生成的进程组,不受控制终端的影响,写写自己的业务逻辑代码
for ($i = 1; $i <= 100; $i++) {
sleep(1);
file_put_contents('daemon.log',$i . "--" . date("Y-m-d H:i:s", time()) . "\n",FILE_APPEND);
}
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
<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>
所以,现在即使关闭了终端,改脚本任然在后台守护进程运行
以上がPHP7におけるデーモン実装の詳細説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。