ホームページ >php教程 >php手册 >デーモンプログラムを記述するためのPHPスクリプト

デーモンプログラムを記述するためのPHPスクリプト

WBOY
WBOYオリジナル
2016-06-21 08:51:59988ブラウズ

これも興味深い概念です。デーモンは英語で「エルフ」を意味します。これは、ディズニーのアニメーションでよく見るものと同じで、漫画の主人公の周りを回ってアドバイスを与えるものもあります。時には不運にも柱にぶつかり、時には敵から主人公を救うためにちょっとしたトリックを考え出すことから、デーモンは「守護聖人」と訳されることもあります。そのため、中国でもデーモンプロセスには「エルフプロセス」と訳す人もいますし、「デーモンプロセス」と訳す人もいます。

本物のデーモンと同様に、デーモン プロセスも人々の目から隠れて静かにシステムに貢献することに慣れており、人々はそれらを「バックグラウンド サービス プロセス」と呼ぶこともあります。一般に、デーモン プロセスの寿命は非常に長く、実行された瞬間からシステム全体がシャットダウンされるまで終了しません。有名な Apache や wu-FTP を含むほとんどすべてのサーバー プログラムは、デーモン プロセスの形式で実装されています。 Linux での inetd や ftpd などの多くの一般的なコマンドでは、末尾の文字 d はデーモンを指します。

なぜデーモンプロセスを使用する必要があるのでしょうか? Linux の各システムがユーザーと通信するためのインターフェイスはターミナルと呼ばれ、このターミナルから実行を開始するすべてのプロセスは、これらのプロセスの制御ターミナルと呼ばれます。 (制御端末)、制御端末を閉じると、対応するプロセスが自動的に終了します。この点に関しては、読者は X-Window の XTerm を使って試すことができます (各 XTerm はオープン ターミナルです)。次のようなコマンドを入力してアプリケーションを起動できます。 $netscape 次に、XTerm ウィンドウと起動したばかりの Netscape を閉じます。窓も一緒に突然蒸発してしまいます。ただし、デーモンプロセスは、対応するターミナルが閉じられている場合でも、ユーザーの影響を受けずにシステム内に長時間存在することができます。ターミナルまたはその他の変更が影響を受ける場合は、このプロセスをデーモン プロセスに変える必要があります。

1. デーモンプロセスのプログラミングルール

プロセスをデーモン プロセスに変えたい場合は、次の手順に厳密に従う必要があります:

1. fork を呼び出して子プロセスを生成すると、同時に親プロセスが終了します。その後の作業はすべて子プロセスで行われます。これを行うには、次のことができます:

1.1 コマンドラインからプログラムを実行すると、プログラムが実行されたかのような錯覚が生じ、シェルは戻って次のコマンドを待ちます。

1.2 フォークを通じて生成されたばかりの新しいプロセスは、プロセス グループのリーダーには絶対にならないため、ステップ 2 の実行の前提条件が保証されます。

これを行うと、非常に興味深い現象も発生します。親プロセスが子プロセスより先に終了しているため、子プロセスには親プロセスがなく、孤立プロセス (孤立) になります。システムが孤立プロセスを見つけると、そのプロセスは自動的にプロセス 1 に採用されます。このようにして、元の子プロセスはプロセス 1 の子プロセスになります。

2. setsid システムコールを呼び出します。

これはプロセス全体の中で最も重要なステップです。 setsid の概要については、付録 2 を参照してください。setsid の機能は、新しいセッションを作成し、セッション リーダーとして機能することです。呼び出しプロセスがプロセス グループのリーダーである場合、呼び出しは失敗しますが、これは手順 1 で保証されています。 setsid の呼び出しには 3 つの機能があります:

2.1 プロセスに元のセッションの制御を解除させます;

2.2 プロセスに元のプロセス グループの制御を解除させます。

2.3 プロセスに元の制御端末の制御を削除させます;

つまり、呼び出し元のプロセスを完全に独立させ、他のすべてのプロセスの制御の対象外にすることです。

3. 現在の作業ディレクトリをルート ディレクトリに切り替えます。

/mnt/floppy/ など、一時的にロードされたファイル システム上でこのプロセスを実行すると、プロセスの現在の作業ディレクトリは /mnt/floppy/ になります。このファイルシステムを使用しているかどうかにかかわらず、プロセス全体でファイルシステムをアンマウント(umount)することはできません。これは非常に不便です。解決策は、chdir システム コールを使用して現在の作業ディレクトリをルート ディレクトリに変更することです。ルート ディレクトリを削除したい人はいないでしょう。

chdir の使用方法については、付録 1 を参照してください。

もちろん、このステップでは、特別な必要がある場合、現在の作業ディレクトリを /tmp などの別のパスに変更することもできます。

4. ファイル許可マスクを 0 に設定します。

これには、システム コール umask を呼び出す必要があります。付録 3 を参照してください。各プロセスは、親プロセスからファイル許可マスクを継承します。新しいファイルが作成されると、このマスクはファイルのデフォルトのアクセス許可を設定し、一般ユーザーの書き込み許可などの特定の許可をブロックするために使用されます。別のプロセスが exec を使用して、私たちが作成したデーモン プログラムを呼び出すと、そのプロセスのファイル許可マスクが何であるかがわからないため、新しいファイルを作成するときに何らかの問題が発生します。したがって、ファイル許可マスクをリセットする必要があります。これは任意の値に設定できますが、一般的には、ユーザー操作がブロックされないように、誰もがそれを 0 に設定します。

アプリケーションに新しいファイルの作成やファイル アクセス許可の設定がまったく含まれていない場合は、ファイル許可マスクをオフにしてこの手順をスキップできます。

5. 不要なファイルをすべて閉じます。

ファイル許可マスクと同様に、新しいプロセスは、親プロセスからすでに開いているファイルの一部を継承します。これらの開かれたファイルは、デーモン プロセスによって読み書きされることはありませんが、それでもシステム リソースを消費し、ファイルが配置されているファイル システムをアンマウントできなくなる可能性があります。ファイル記述子 0、1、2 を持つ 3 つのファイル (ファイル記述子の概念については次の章で説明します) (通常、入力ファイル、出力ファイル、エラー ファイルと呼ばれるもの) も閉じる必要があることに注意してください。 。多くの読者はこれについて疑問に思うかもしれませんが、入力と出力は必要ないのでしょうか? しかし、実際には、上記のステップ 2 の後、デーモン プロセスは、それが属する制御端末との接続を失うことになります。デーモンプロセスに到達する文字が多く、デーモンプロセスが従来の方法(printfなど)で出力した文字は、弊社の端末では表示できません。したがって、これら 3 つのファイルは存在価値を失っているため、閉じるべきです。

PHP を使用して Gearman の Worker デーモンを作成する

前回の記事ではGearmanの使い方を紹介しました。私のプロジェクトでは、PHP を使用して、常に実行されるワーカーを作成します。 Gearman が推奨する例に従い、単純なループでタスクを待機する場合、次のようないくつかの問題が発生します。 1. コードが変更されたとき、コードの変更をどのように有効にするか。 2. ワーカーを再起動するとき。 、タスク処理が完了した後に現在の再起動を確認する方法。

この問題に対応して、次の解決策を検討しました:

1. コードを変更するたびに、ワーカーを手動で再起動する必要があります (最初に強制終了してから開始します)。これは、構成ファイルの再ロードの問題のみを解決します。

2. Worker に設定し、1 つのタスク サイクルが完了した後に Worker を再起動します。このソリューションの問題は、多額の費用がかかることです。

3. ワーカーに終了関数を追加します。ワーカーが終了する必要がある場合は、より高い優先順位で終了呼び出しをクライアントに送信します。これにはクライアントの協力が必要ですが、バックグラウンド タスクを使用する場合には適していません。

4. Worker でファイルが変更されているかどうかを確認し、変更されている場合は、Worker を終了して再起動します。

5. Worker のシグナル制御を書き込み、http の正常な再起動命令と同様の再起動命令を受け入れます。

最後に、2 つの方法 4 と 5 を組み合わせることにより、このようなデーモンを実装できます。設定ファイルが変更された場合、ユーザーの kill -1 pid シグナルを受信すると、デーモンも自動的に再起動します。

コードは次のとおりです:

  1. declare( ticks = 1 );  
  2. // このケースでは、設定ファイルを定期的にチェックし、設定ファイルが変更された場合は自動的に再起動します
  3. // デーモンを正常に再起動したい場合は、HUP シグナルを与えます
  4. // by shiqiang<cocowool@gmail.com> at 2011-12-04
  5. $init_md5 = md5_file( 'config.php');  
  6. // シグナルハンドラーを登録します
  7. pcntl_signal( SIGALRM, "signal_handler", true );  
  8. pcntl_signal( SIGHUP, 'signal_handler', TRUE );  
  9. $job_flag = FALSE;   
  10. //ジョブが完了したかどうかを正当化するためのジョブステータスフラグ
  11. $signal_flag = FALSE;   
  12. //kill -1 シグナルを受信したかどうかを正当化するためのシグナルステータスフラグ
  13. while( 1 ){
  14. $job_flag = FALSE;   
  15. //ジョブステータスフラグ
  16. print "ワーカーが実行を開始... n";  
  17. 睡眠(5);  
  18. print 「作業者のタスクは完了しました... n」;  
  19. $フラグ = TRUE;  
  20. //ジョブステータスフラグ
  21. AutoStart( $signal_flag );  
  22. function signal_handler( $signal ) {
  23. グローバル $job_flag;  
  24. グローバル $signal_flag;  
  25. switch( $signal ){
  26. ケース SIGQUIT:
  27. print date('y-m-d H:i:s', time() ) 。 " キャッチされた シグナル : SIGQUIT - いいえ : $signal n";  
  28. exit(0);  
  29. 休憩;  
  30. ケース SIGSTOP:
  31. print date('y-m-d H:i:s', time() ) 。 " 信号をキャッチしました: SIGSTOP - いいえ: $signal n";  
  32. 休憩;  
  33. ケース ため息:
  34. print date('y-m-d H:i:s', time() ) 。 " 信号をキャッチしました: ため息 - いいえ: $signal n";  
  35. if( $フラグ === TRUE ){
  36. AutoStart( TRUE );  
  37. }else{
  38. $signal_flag = TRUE;  
  39. }
  40. 休憩;  
  41. ケース SIGALRM:
  42. print date('y-m-d H:i:s', time() ) 。 " キャッチされた シグナル : SIGALRM - いいえ : $signal n";  
  43. //pcntl_exec( '/bin/ls' );  
  44. pcntl_alarm( 5 );  
  45. 休憩;  
  46. デフォルト:
  47. 休憩;  
  48. }
  49. 関数 AutoStart( $信号 = FALSE, $ファイル名 = 'config.php' ){
  50. グローバル $init_md5;  
  51. if( $signal md5_file( $filename ) != $init_md5 ){
  52. print 「設定ファイルが変更されました。再起動します。n」;  
  53. $pid = pcntl_fork();  
  54. if( $pid == -1 ){
  55. print "フォークエラーn";  
  56. }else if( $pid > 0 ){
  57. print 「親 exit n」;  
  58. exit(0);  
  59. }else{
  60. $init_md5 = md5_file( $filename );  
  61. print 「子供は走り続けます」;  
  62. }
  63. }
  64. }



声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。