POSIXでは、プロセスは次の2つの基本的な階層を介して互いに「関連しています」。
親プロセスと子プロセスの階層。
セッションとプロセスグループの階層。
ユーザープロセスは、setpgid
とsetsid
を介して後者をかなり制御できますが、前者はほとんど制御できません。親プロセスIDは、プロセスが生成されたときに設定され、親が終了したときにカーネルによって変更されます(通常、 PID 1)、ただしそれ以外は変更されません。それを振り返って、親子関係が本当に重要なのかと思っていました。
これまでの私の理解の要約は次のとおりです。
wait
やsetpgid
などのさまざまなシステムコールは子プロセスでのみ許可されるため、親子関係はparentプロセスの観点から明らかに重要です。
kill
のようなsyscallは全体で動作するため、セッションとグループとプロセスの関係は、allプロセス(セッションリーダーとセッション内の他のプロセスの両方)にとって明らかに重要です。プロセスグループ、setpgid
は、同じセッションのグループに参加するためにのみ使用でき、セッションリーダーが終了した場合、セッションのフォアグラウンドプロセスグループのすべてのプロセスにSIGHUP
が送信されます。
さらに、setsid
は新しい子にのみ影響し、setpgid
は子にのみ使用できるため、2つの階層は親の観点から明らかに関連しています。ただし、それらは子の観点からは本質的に関連がないようです(親プロセスが終了しないため、プロセスのグループまたはセッションに影響を与える)。
ただし、不在が目立つのは、子プロセスが現在の親を気にする必要がある理由です。したがって、次の質問があります:子プロセスの観点から見ると、getppid()
の現在の値は何でも重要ですおそらく、その生成プロセスが終了したかどうかを識別することに加えて?
同じ質問を別の言い方で言うと、同じプログラムが2つの異なる方法で同じ親から2回生成されたとします。
最初の子は、通常の方法でfork()
によって生成され、その後すぐにexec()
によって生成されます。
2番目の子は間接的に生成されます。親プロセスがfork()
を呼び出し、次に子alsoがfork()
を呼び出します、そしてexec()
を呼び出すのはgrandchildプロセスです。次に、直接の子が終了するため、孫は孤立し、そのPPIDはPID 1に再割り当てされます。
この架空のシナリオでは、他のすべてが等しいと仮定して、合理的なプログラムは異なる動作をする理由がありますか?これまでのところ、セッションは変更されていないため、私の結論は「ノー」のようです。プロセスの継承されたファイル記述子です…しかし、私にはわかりません。
注:孤立したプログラムは、一般にPPIDが1に設定されていることに依存できないため、「親のPIDを取得して通信する」とは考えません。そのため、競合状態を回避する唯一の方法は、getpid()
beforeを呼び出して親プロセスIDを取得することですフォークして、その値を子で使用します。
この質問を見たとき、私はknow以前にgetppidが使用されたのを見たことがあるので、かなり興味を持っていました。しかし、どこで覚えているかわかりませんでした。それで、私はおそらくすべてのLinux syscallを使用しており、次にいくつかを使用していると考えたプロジェクトの1つに目を向けました: systemd 。 1つ GitHub search 後で、より一般的なユースケースを表す2つの使用方法を見つけました(他にもいくつかの使用方法がありますが、systemdにより固有です)。
sd-notify で一部のコンテキストでは、systemdはサービスがいつ開始されたかを知る必要があるため、サービスに依存するサービスを開始できます。これは通常、デーモンがsystemdにステータスを通知する方法である sd_notify API を介してCプログラムから実行されます。
もちろん、シェルスクリプトをサービスとして使用している場合、C関数の呼び出しは正確に実行できません。したがって、systemdには systemd-notifyコマンド が付属しています。これは、sd_notify APIの小さなラッパーです。 1つの問題:systemdはメッセージを送信しているPIDも知る必要があります。 systemd-notifyの場合、これは独自のPIDになります。これは、すぐに消える短期間のプロセスIDです。役に立たない。
あなたはおそらく私がどこに向かっているのかすでに知っています:getppidは親プロセスのPIDを取得するためにsystemd-notifyによって使用されます。これは通常、それが実際のサービスプロセスだからです。つまり、getppidは、存続期間の短いCLIアプリケーションが親プロセスに代わってメッセージを送信するために使用できます。
これを見つけたとき、このようなgetppidを使用する可能性のある別のUNIXツールが思い浮かびました:polkitは、D-Busメッセージの送信や特権付きアプリケーションの実行などを制御するために使用されるプロセス認証フレームワークです。 (少なくとも、polkitの認証エージェントによって表示されるGUIパスワードプロンプトを見たことがあると思います。)polkitには、Sudoのように使用できるpkexec
という実行可能ファイルが含まれていますが、現在はpolkitが使用されています。認可のため。今、polkitは承認を要求するプロセスのPIDを知る必要があります...そうです、 pkexecはgetppidを使用してそれを見つけます 。
(それを見ていると、 polkitのTTY認証エージェントもそれを使用していることがわかりました 。)
これは少し面白くありませんが、それでも注目に値します。フラグが設定されるまでに親が死亡した場合、getppidを使用して PR_SET_PDEATHSIG をエミュレートします。 (フラグは、親が死んだ場合に子がSIGKILLのようなシグナルを自動的に送信される方法にすぎません。)
プロセスが親、祖父母など、システム内の他のプロセスの認識を必要とするシナリオの範囲には、プロセス管理やプロセス間通信が含まれるようです。スコープをさらに狭めるために、pid_t
を引数として取る、またはpid_t
を返す操作の範囲は、主にシグナリングおよびその他のプロセス管理タスクです。このように使用範囲が限られている場合、診断情報以外にgetppid
を使用する理由として考えられるのは、子が親に信号を送る必要があるかどうか、または親がまだ実行中かどうかを判断する場合です。たとえば、Apacheのmod_md
httpd
グレースフルリスタートシグナルを親プロセスに送信 再構成をトリガーします。
質問の「メモ」はgetppid
に代わる優れた方法を説明していますが、getppid
の可能な使用法のヒントにもなっています。コンテキストについては、昨夜のPWL Conf 2019からのプレゼンテーション On the Expressive Power of Programming Languages を見ていて、その論文の「表現力」の定義が私の質問の解釈に偏っています。定義は、機能の存在の有無にかかわらず、プログラムの動作がどのように異なるかを要約しています。
前提条件から始めます。
Pre _fork
PIDがgetppid
と一致しないとはどういう意味ですか? pre -fork
PIDとgetppid
を比較すると、子プロセスが孤立したかどうかを判断するために、クロスプラットフォームで競合のないメカニズムが実際に提供される可能性があります。 pre-fork-pid != getppid
の場合、子は孤立します。それ以外の場合は孤立しません。
これは珍しいことのように思えるか、または必要に応じてプラットフォーム固有のコンパイル#ifdef
を使用して記述される可能性があるもののようです。また、前のfork
-pid
にkill 0
を使用しても同様の効果が得られますが、これにより、システムが「古い」を再利用するときに、十分に長い実行プロセスで誤検知が発生する可能性がありますプロセスID。
したがって、不変のpre _fork
親プロセスIDと「ライブ」getppid
呼び出しの組み合わせは、それを確認またはシグナリングするための信頼性の高いクロスプラットフォームメカニズムである可能性があるように見えます親プロセス。
ゾンビ:これはエッジのケースですが、プログラムが停止するかどうかに影響するという意味で効果的です。子プロセスの動作が、親プロセスの親プロセスに基づいて異なる唯一の状況は、終了時です。プロセスが終了すると、SIGCHLD
が子のppid
に送信されます。親プロセスが割り込まれているか、SIGCHLD
を処理していない場合、子は、終了シグナルを受信するまでゾンビ状態のままになります。ゾンビ状態のときに子のppid
が変更された場合、親プロセスを強制終了してinit
に親を変更し、SIGCHLD
を受信すると、子供は終了し、刈り取られます。