デーモン: 通常、バックグラウンド プロセスとして定義され、どのターミナル セッションにも属しません。ネットワーク サービスや印刷など、多くのシステム サービスはデーモンによって実装されます。 次の記事では、Python がデーモン プロセスを実装する方法の例を紹介します。必要な方は参照してください。
シナリオ設定:
Python サービス プログラムを作成してコマンド ラインから開始すると、コマンド ライン セッションはターミナルによって制御され、Python サービスはターミナル プログラムのサブプロセスになります。したがって、ターミナルを閉じると、コマンド ライン プログラムも閉じられます。
ターミナルの影響を受けずに Python サービスをシステム内で永続的にするには、Python サービスをデーモン プロセスに変える必要があります。
デーモンプロセスは、システムのバックグラウンドで実行されるプログラムであり、制御端末から独立して、いくつかの定期的なタスクを実行したり、イベントをトリガーしたりします。 "、一般的な 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)
デーモン プロセスの作成手順を要約します。 子プロセスをフォークし、親プロセスを終了します
子プロセスは、作業ディレクトリ (chdir)、ファイル許可マスク (umask)、プロセス グループおよびセッション グループ (setsid) を変更します
子プロセスは、孫プロセスと子プロセスを終了します プロセス
孫プロセスはバッファを更新し、標準入出力/エラーをリダイレクトします (通常は /dev/null、つまり破棄されます)
(オプション) pid がファイルに書き込まれます
なぜ 2 回フォークするのか
最初のフォークは、端末制御のクラッチから逃れるためです。親プロセスが終了する理由は、ターミナルが閉じるときにキーボードを押すかシグナルを送信し、親プロセスが自殺した後、フォークされた子プロセスが孤立プロセスになり、init プロセスに引き継がれるためです。オペレーティング システムの制御を終了するため、端末の制御を離れます。
つまり、実際には 2 回目のフォークは必要ありません (多くのオープンソース プロジェクトのコードは 2 回フォークしません)。これは、プロセスが再び制御端末を開かないようにするための警戒心からです。子プロセスは現在セッション リーダー (セッション内の最初のプロセス) であり、制御端末を開くことができるため、子プロセスが再びフォークすると、孫プロセスは制御端末を開くことができなくなります。Linux は「すべてがファイルである」ファイル記述子は、開いているファイルに対してカーネルによって作成されるインデックスであり、通常は非負の整数です。プロセスは、ファイル記述子を通じて IO 操作を実行します。
デフォルトでは、0 は標準入力、1 は標準出力、2 は標準誤差を表します。Linux では、どのファイルにも読み取り (読み取り)、書き込み (書き込み)、実行 (実行) の 3 つの権限があることがわかっています。このうち、読み取り権限は番号 4、書き込み権限は 2、実行権限は 1 で表されます。ファイル権限は ls -l コマンドで確認できます。r/w/x は、それぞれ読み取り/書き込み/実行権限があることを意味します。
どのファイルにも、ユーザー、グループ、その他の 3 つの ID 権限があります。一般に、754 などの 3 つの数字がファイル権限を表すために使用されます:
7、ユーザー権限、つまりファイル所有者の権限
各プロセスはプロセスグループ(PG、プロセスグループ)に属しており、プロセスグループには複数のプロセスを含めることができます。 プロセスグループにはプロセスリーダー(Leader)があり、プロセスリーダーのID(PID、Process ID)がプロセスグループ全体のID(PGID、Process Groupd ID)として使用されます。
セッショングループ
ターミナルにログインすると、1つのセッションに複数のプロセスグループを含めることができます。セッションを作成するプロセスがセッション リーダーです。 すでにセッション リーダーであるプロセスは、setsid() メソッドを呼び出してセッションを作成できません。したがって、上記のコードでは、子プロセスは setid() を呼び出すことができますが、親プロセスはセッション リーダーであるため、呼び出すことができません。
さらに、sh (Bourne Shell) はセッションメカニズムをサポートしていません。これは、セッションメカニズムではシェルがジョブ制御 (Job Control) をサポートする必要があるためです。
&記号を使用すると、コマンドをバックグラウンドで実行できます。デーモンとは異なります: デーモンプロセスはターミナルとは何の関係もなく、initプロセスによって採用された孤立プロセスです。バックグラウンドプロセスの親プロセスはターミナルですが、それでもターミナル上で印刷できます nohup が追加されない限り、バックグラウンド プロセスは強力なままです。バックグラウンド プロセスは、セッション、プロセス グループ、作業ディレクトリ、およびファイル記述子を直接継承します。親プロセス(シェル) つまりデーモンプロセス 黙々と働く将来有望な若者たちであり、裏プロセスは父の資産を黙々と受け継ぐ金持ちの二世たち。