次のシナリオを考えてみましょう。Python サービス プログラムを作成してコマンド ラインから開始すると、コマンド ライン セッションはターミナルによって制御され、Python サービスはターミナル プログラムのサブプロセスになります。したがって、ターミナルを閉じると、コマンド ライン プログラムも閉じられます。
ターミナルの影響を受けずに Python サービスをシステム内で永続的にするには、Python サービスをデーモン プロセスに変える必要があります。
デーモンプロセスは、システムのバックグラウンドで実行されるプログラムであり、制御端末から独立しており、いくつかの定期的なタスクを実行したり、イベントをトリガーしたりするため、通常は「d」という文字にちなんで名付けられます。一般的な httpd、syslogd、systemd、dockerd など。
Python はデーモン プロセスを非常に簡単に実装できます。コードと対応するコメントを以下に示します。このコードはローカルコンピュータ上のデーモンプロセス(自作目覚まし時計)で安定して動作しており、今のところ問題はありません。
# coding=utf8 import os import sys import atexit def daemonize(pid_file=None): """ 创建守护进程 :param pid_file: 保存进程id的文件 :return: """ # 从父进程fork一个子进程出来 pid = os.fork() # 子进程的pid一定为0,父进程大于0 if pid: # 退出父进程,sys.exit()方法比os._exit()方法会多执行一些刷新缓冲工作 sys.exit(0) # 子进程默认继承父进程的工作目录,最好是变更到根目录,否则回影响文件系统的卸载 os.chdir('/') # 子进程默认继承父进程的umask(文件权限掩码),重设为0(完全控制),以免影响程序读写文件 os.umask(0) # 让子进程成为新的会话组长和进程组长 os.setsid() # 注意了,这里是第2次fork,也就是子进程的子进程,我们把它叫为孙子进程 _pid = os.fork() if _pid: # 退出子进程 sys.exit(0) # 此时,孙子进程已经是守护进程了,接下来重定向标准输入、输出、错误的描述符(是重定向而不是关闭, 这样可以避免程序在 print 的时候出错) # 刷新缓冲区先,小心使得万年船 sys.stdout.flush() sys.stderr.flush() # dup2函数原子化地关闭和复制文件描述符,重定向到/dev/nul,即丢弃所有输入输出 with open('/dev/null') as read_null, open('/dev/null', 'w') as write_null: os.dup2(read_null.fileno(), sys.stdin.fileno()) os.dup2(write_null.fileno(), sys.stdout.fileno()) os.dup2(write_null.fileno(), sys.stderr.fileno()) # 写入pid文件 if pid_file: with open(pid_file, 'w+') as f: f.write(str(os.getpid())) # 注册退出函数,进程异常退出时移除pid文件 atexit.register(os.remove, pid_file)
デーモン プロセスを作成する手順を要約します。
1. 子プロセスをフォークアウトし、親プロセスを終了します
2. 子プロセスは、作業ディレクトリ (chdir)、ファイル許可マスク (umask)、プロセス グループを変更します。およびセッション グループ (setsid)
3. 子プロセスは孫プロセスをフォークし、子プロセスを終了します
4. 孫プロセスはバッファを更新し、標準入力/出力/エラーをリダイレクトします (通常は /dev/null を意味します)。破棄)
5. ファイルに書き込む pid を (選択可能)
最初のフォークは、端末制御のクラッチから逃れるためです。親プロセスが終了する理由は、ターミナルが閉じるときにキーボードを押すかシグナルを送信し、親プロセスが自殺した後、フォークされた子プロセスが孤立プロセスになり、init プロセスに引き継がれるためです。オペレーティング システムの制御を終了するため、端末の制御を離れます。
つまり、実際には 2 回目のフォークは必要ありません (多くのオープンソース プロジェクトのコードは 2 回フォークしません)。これは、プロセスが再び制御端末を開かないようにするための警戒心からです。子プロセスは現在セッション リーダー (セッション内の最初のプロセス) であり、制御端末を開くことができるため、子プロセスが再びフォークすると、孫プロセスは制御端末を開くことができなくなります。
Linux は「すべてがファイルである」です。ファイル記述子は、開かれたファイルに対してカーネルによって作成されるインデックスであり、通常は非負の整数です。プロセスはファイル記述子を通じて IO 操作を実行します。
各プロセスには独自のファイル記述子テーブルがあるため、同じ記述子が同じファイルを指すことも、異なるファイルを指すこともできます。もちろん、異なるプロセスの異なる記述子が同じファイルを指すこともあります。
デフォルトでは、0 は標準入力、1 は標準出力、2 は標準誤差を表します。
Linux では、どのファイルにも読み取り、書き込み、実行という 3 つの権限があることがわかっています。このうち、読み取り権限は番号 4、書き込み権限は 2、実行権限は 1 で表されます。ファイルの権限は ls -l コマンドで確認できます。r/w/x は、それぞれ読み取り/書き込み/実行権限があることを意味します。
どのファイルにも、ユーザー、グループ、その他という 3 つの ID 権限があります。一般に、754 のような 3 つの数字がファイルの権限を表すのに使用されます:
7、これはユーザー権限、つまりファイル所有者の権限です。
5、これはグループ権限、つまりメンバーの権限です。所有者のユーザー グループ
4、つまりその他のアクセス許可は、他のグループのユーザーのアクセス許可です
そして、umask はデフォルトのアクセス許可を制御し、新しいファイルやフォルダーが完全なアクセス許可を持たないようにすることです。
システムのデフォルトは通常 022 (表示するにはコマンド umask を使用) です。これは、ファイル作成のデフォルトの権限が 644、フォルダーのデフォルトの権限が 755 であることを意味します。ファイルのパーミッションと umask の合計が 666 (笑)、フォルダーのパーミッションと umask の合計が 777 であるというパターンがわかるはずです。
各プロセスはプロセスグループ(PG、プロセスグループ)に属しており、プロセスグループには複数のプロセスを含めることができます。
プロセスグループにはプロセスリーダー(Leader)があり、プロセスリーダーのID(PID、Process ID)がプロセスグループ全体のID(PGID、Process Groupd ID)として使用されます。
ターミナルにログインすると、1つのセッションに複数のプロセスグループを含めることができます。セッションを作成するプロセスがセッション リーダーです。
すでにセッション リーダーであるプロセスは、setsid() メソッドを呼び出してセッションを作成できません。したがって、上記のコードでは、子プロセスは setid() を呼び出すことができますが、親プロセスはセッション リーダーであるため、呼び出すことができません。
さらに、sh (Bourne Shell) はセッションメカニズムをサポートしていません。これは、セッションメカニズムではシェルがジョブ制御 (Job Control) をサポートする必要があるためです。
&マークを使用すると、バックグラウンドでコマンドを実行できます。デーモン プロセスとは異なります。
1. デーモン プロセスは端末とは何の関係もなく、init プロセスによって採用された孤立プロセスですが、バックグラウンド プロセスの親プロセスは端末であり続けます。ターミナル
2. デーモンプロセスはターミナルを閉じます。ユーザーが終了すると、バックグラウンドプロセスは、nohup が追加されていない限り停止します
3. デーモンプロセスは、セッション、プロセスグループを変更します。作業ディレクトリとファイル記述子、およびバックグラウンド プロセスは親プロセス (シェル) の
を直接継承します。言い換えれば、デーモンプロセスは黙々と働く有望な若者であり、バックグラウンドプロセスは父親の資産を黙って引き継ぐ裕福な二世です。
以上がPythonデーモンの実装の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。