新しいプロセスは、スレッドを作成したメイン スレッドの子になります。と思います。
fork
新しいプロセスを作成します。プロセスの親は、スレッドではなく別のプロセスです。したがって、新しいプロセスの親は古いプロセスです。
fork
であるため、子プロセスには 1 つのスレッドしかないことに注意してください。 fork
を呼び出すスレッド (のスタック) のみを複製します . (これは完全に正しいわけではありません。メモリ全体が複製されますが、子プロセスにはアクティブなスレッドが 1 つしかありません。)
その親プロセスが最初に終了した場合、新しいプロセスは init プロセスにアタッチされます。
親が最初に終了した場合 SIGHUP
子供に信号を送ります。 SIGHUP
の結果として子が終了しない場合 init
になります 新しい親として。 nohup
の man ページも参照してください。 と signal(7)
SIGHUP
の詳細については、 .
そして、その親はそれを作成したスレッドではなく、メイン スレッドです。
プロセスの親はプロセスであり、特定のスレッドではないため、メイン スレッドまたは子スレッドが親であると言っても意味がありません。プロセス全体が親です。
最後に 1 つ:スレッドとフォークの混合は注意して行う必要があります。ここでは、いくつかの落とし穴について説明します。
<ブロック引用>
間違っていたら訂正してください。
やります:)
fork()
として は POSIX システム コールであり、その動作は明確に定義されています:
プロセスは単一のスレッドで作成されます .マルチスレッド プロセスが fork() を呼び出す場合、新しいプロセスには、呼び出し元のスレッドのレプリカとそのアドレス空間全体が含まれ、ミューテックスやその他のリソースの状態が含まれる可能性があります。したがって、エラーを回避するために、子プロセスは、exec 関数の 1 つが呼び出されるまで、非同期シグナルセーフ操作のみを実行できます。
https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html
フォークされた子はその親の正確な複製ですが、fork()
を呼び出したスレッドのみです。 exec()
を呼び出すまで、親ではまだ子に存在し、その子の新しいメイン スレッドです。 .
実際、ほとんどの実装では親プロセスの正確な複製が作成されるため、POSIX の説明「単一のスレッドで作成する必要がある」は誤解を招きやすいため、他のすべてのスレッドとそのメモリも同様に複製されます。つまり、スレッドが実際に存在することを意味します。 、システムがCPU時間を割り当てないため、もはや実行できません。実際、これらはカーネル スレッド スケジューラ テーブルにありません。
より簡単なイメージは次のとおりです:
親が fork を呼び出すと、プロセス全体が一瞬凍結され、アトミックに複製されます。その後、親は全体として凍結解除されますが、子では fork を呼び出した 1 つのスレッドのみが凍結解除され、他のすべては凍結されたままになります。
そのため、fork()
の間で特定のシステム コールを実行するのは安全ではありません。 と exec()
POSIX 標準でも指摘されているとおりです。理想的には、ファイル記述子を閉じたり複製したり、シグナル ハンドラーを設定または復元したり、exec()
を呼び出したりするだけで十分です。 .
<ブロック引用>
しかし、スレッドが fork() を使用して新しいプロセスを作成するとどうなるでしょうか?
呼び出し元の スレッド をコピーすることにより、新しいプロセスが作成されます アドレス空間 (プロセス のアドレス空間全体ではありません) )。それを正しく行うのは非常に難しいため、一般的には悪い考えと見なされます。 POSIX によると、(マルチスレッド プログラムで作成された) 子プロセスは、exec*
のいずれかを呼び出すまで、async-signal-safe 関数のみを呼び出すことができます。 関数。
その親プロセスが先に終了すると、新しいプロセスが initprocess にアタッチされます。
通常、子プロセスは init プロセスによって継承されます。親プロセスが制御プロセスである場合 (シェルなど) の場合、POSIX では以下が必要です:
<ブロック引用>プロセスが制御プロセスの場合、SIGHUP シグナルは、呼び出しプロセスに属する制御端末のフォアグラウンド プロセス グループ内の各プロセスに送信されます。
ただし、ほとんどのプロセスはプロセスを制御していないため、これはほとんどのプロセスには当てはまりません。
<ブロック引用>そして、その親はそれを作成したスレッドではなく、メイン スレッドです。
fork された子の親は、常に fork() を呼び出したプロセスになります。したがって、PPID は子プロセスであり、プログラムの PID になります。
<ブロック引用>
問題は、fork(2) 自体の動作に起因します。新しい子プロセスが fork(2) で作成されるたびに、新しいプロセスは新しいメモリ アドレス空間を取得しますが、メモリ内のすべてが古いプロセスからコピーされます (コピー オン ライトは 100% 真ではありませんが、セマンティクスは同じです)。
マルチスレッド環境で fork(2) を呼び出すと、呼び出しを行っているスレッドが新しいプロセスのメインスレッドになり、親プロセスで実行されていた他のすべてのスレッドが停止します。そして、彼らが行ったことはすべて、fork(2) を呼び出す直前の状態のままでした。
ここで、これらの他のスレッドが fork(2) の呼び出しの前に喜んで作業を行っていて、数ミリ秒後に死んでいると想像してください。これらの今は死んでいるスレッドが行ったことを、そのままにしておくことを意図していなかったとしたら?
例を挙げましょう。メインスレッド (fork(2) を呼び出すスレッド) がスリープ状態で、他の多くのスレッドが喜んで何らかの作業を行っているとします。メモリの割り当て、書き込み、コピー、ファイルへの書き込み、データベースへの書き込みなどです。おそらく、malloc(3)のようなものでメモリを割り当てていました。まあ、malloc(3)は内部でミューテックスを使用していることがわかりました。スレッドセーフを保証します。まさにこれが問題です。
これらのスレッドの 1 つが malloc(3) を使用していて、メインスレッドが fork(2) を呼び出したのとまったく同じ瞬間にミューテックスのロックを取得した場合はどうなるでしょうか?新しい子プロセスでは、ロックはまだ保持されています - もはや死んでいるスレッドによって、ロックは決して返されません。
新しい子プロセスは、malloc(3) を使用しても安全かどうかわかりません。最悪の場合、それは malloc(3) を呼び出し、ロックを取得するまでブロックしますが、ロックを返すはずのスレッドが停止しているため、ロックが発生することはありません。そして、これは単なる malloc(3) です。データベース ドライバ、ファイル処理ライブラリ、ネットワーク ライブラリなどで考えられる他のすべてのミューテックスとロックについて考えてみてください。
完全な説明については、このリンクからアクセスできます。