マルチコアでコンパイルするときにmakeがクラッシュする原因は何ですか?

マルチコアでコンパイルするときにmakeがクラッシュする原因は何ですか?

昨日はコンパイルしようとしました。ソースのパッケージ。 6コアモンスターマシンでコンパイルしていたので使い続けることにしましたmake -j 6。最初はコンパイルがスムーズで非常に高速でしたが、ある時点でmake1つのコアで100%CPUを使用すると中断されました。

Googleを少し探していました。これROOT掲示板に投稿してください。このコンピュータを自分で作ってみると、ヒートシンクを正しく取り付けずにCPUが過熱するなどの心配があります。残念ながら、私が働く場所に入れることができる冷蔵庫はありません。 ;-)

パッケージをインストールlm-sensorsしてmake -j 6再実行しましたが、今回はCPU温度を監視しました。温度は非常に高いですが(ほぼ60℃)、決して高い温度や臨界温度を超えません。

私はrunningを試しましたが、make -j 4コンパイル中のある時点でmake今回は別の場所で再び停止しました。

最後に、これをコンパイルして実行しましたが、makeうまくいきました。私の質問は:なぜ停止しますか? 2つの異なる場所で停止するため、競合状態の種類があると思いますが、make提供されているオプションを考慮すると、すべてを正しい順序でインポートするのに十分スマートでなければならないと思いました-j

答え1

この正確な質問に対する答えはありませんが、何が起こるのかについてのヒントを提供できます。 Makefileに依存関係がありません。

例:

target: a.bytecode b.bytecode
    link a.bytecode b.bytecode -o target

a.bytecode: a.source
    compile a.source -o a.bytecode

b.bytecode: b.source
    compile b.source a.bytecode -o a.bytecode

呼び出すと、make targetすべてが正しくコンパイルされます。a.sourceコンパイルは最初に実行されます(任意ですが決定論的に)。その後、b.sourceコンパイルします。

ただし、make -j2 target両方のcompileコマンドが同時に実行される場合。実際、Makefileの依存関係が破損していることがわかります。 2番目のコンパイルではコンパイルさa.bytecodeれたと仮定しますが、依存関係には表示されません。したがって、エラーが発生する可能性が高いです。正しい依存関係行は次b.bytecodeのようになります。

b.bytecode: b.source a.bytecode

質問に戻って、運が悪いと、依存関係がないため、命令が100%CPUループで中断される可能性があります。これがおそらくここで起こっていることでしょう。順次ビルドでは欠落している依存関係を明らかにすることはできませんでしたが、並列ビルドではすでにこれを明らかにしました。

答え2

これは非常に古い質問であることを知っていますが、まだ検索結果の上部に表示されるので、私の解決策は次のとおりです。

GNU make には、 make とその再帰サブプロセスが指定されたコア数を超えないようにするための Job Server メカニズムがあります。 http://make.mad-scientist.net/papers/jobserver-implementation/

すべてのプロセスが共有するパイプに依存します。追加の子プロセスをフォークしたい各プロセスは、まずパイプでトークンを使用し、完了したら廃棄する必要があります。子プロセスが消費したトークンを返さない場合、最上位のmakeは永久に停止し、返されるのを待ちます。

https://bugzilla.redhat.com/show_bug.cgi?id=654822

「sed」がGNU sedではなく、SolarisシステムでGNU makeを使用してbinutilsをビルドすると、このエラーが発生しました。この問題は、sed = = gsedがシステムsedよりも優先されるようにPATHを修正することによって解決されました。しかし、sedがパイプからトークンを消費する理由はわかりません。

答え3

makeデッドロックを作ったようです。を使用すると、ps -ef次のプロセスが原因のようです。

ルート695 615 1 22:18? 00:00:00 prebuild-j32を作成
ルート2127 695 20 22:18 ? 00:00:04 make -f Makefile.prenobuild

各子プロセスが実行する操作を確認すると、子プロセスはファイル記述子4に書き込まれますが、親プロセスはすべての子プロセスが終了するのを待っていることがわかります。

root@ltzj2-6hl3t-b98zz:/# strace -p 2127
strace: 接続プロセス 2127
書き込み(4,"+",1
root@ltzj2-6hl3t-b98zz:/# strace -p 695
strace: プロセス 695 接続
{{しばらく 4(-1, }})

ファイル記述子4はパイプである。

root@ltzj2-6hl3t-b98zz:/# ls -la /proc/2127/fd/4
l-wx------ 1 root root 64 Sep 3 22:22 /proc/2127/fd/4 -> 'pipe:[1393418985]'

パイプは親プロセスと子プロセスの間にのみ存在します。

root@ltzj2-6hl3t-b98zz:/#lsof 1393418985 |
695 ルート 3r FIFO 0,12 0t0 1393418985 パイプを作る
695 ルート 4w FIFO 0,12 0t0 1393418985 パイプを作る
2127 ルート 3r FIFO 0,12 0t0 1393418985 パイプを作る
2127 ルート 4w FIFO 0,12 0t0 1393418985 パイプを作る

したがって、2127は695に再びパイプに出力を追加しようとするようですが、695は保留状態なwait4()のでパイプを消去しません。

cat を使用してシェルからパイプをクリアすると、ビルドが期待どおりに再開され、完了します。

root@ltzj2-6hl3t-b98zz:/#猫/proc/695/fd/3
++++++++++++++++++++++++++++++++++

ビルドがブロック解除され、引き続き実行されます...


私の初期の理解は間違っていましたが、さらに調査した後、最終的に次のLinuxカーネルの欠陥が見つかりました。

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=46c4c9d1beb7f5b4cec4dd90e7728720583ee348

一時停止方法の正確な説明は次のとおりです。https://lore.kernel.org/lkml/1628086770.5rn8p04n6j.none@localhost/

gnu make ソースコードに次の回避策を適用してカーネルパッチを待つこの問題を解決できます。

---a/src/posixos.c 2020-01-02 23:11:27.000000000-0800
+++ b/src/posixos.c 2021-09-18 09:12:02.786563319 -0700
@@-179,8+179,52 @@
 jobserver_release(int is_fatal)
 {
   整数r;
- EINTRLOOP(r、write(job_fds [1]、&token、1));
- if(r != 1)
+整数n;
+文字b [32];
+
+/* 複数の make サブプロセスによるデッドロックを回避するために、非ブロック書き込みを使用します。
+ * また、ジョブを解放します。 */
+ set_blocking(job_fds[1], 0);
+ memset(b,token,sizeof(b));
+ n = 1;
+同時に(n> 0)
+{
+ r =書き込み(job_fds [1]、b、n);
+/* システムコールが中断されました。もう一度お試しください*/
+ if ( r == -1 )
+{
+ if (エラー番号 == EINTR)
+継続;
+
+ /* このプロセスと他のプロセスが両方ともパイプに書き込もうとしたのでここまで来ました。
+ *まったく同じ時間で、パイプラインには1ページしか含まれていません。私たちは他人を失った
+ *プロセスが勝ちます(パイプに書き込む)。まず、この条件をリセットできます。
+ *パイプから読みます。もちろん、これはさらに返す必要があることを意味します。
+ *トークン。 */
+ if ( errno == EWOULDBLOCK || errno == EAGAIN )
+{
+ if ( jobserver_acquire(0) )
+{
+n++;
+ /*おそらく不可能に近いかもしれません... */
+ if (n > 32)
+割り込み;
+継続;
+}
+}
+}
+ if ( r == 0 ) /* 0バイトを書きましたが、エラーはありませんでしたので、もう一度お試しください */
+継続;
+ if(r> 0)
+{
+ n -= r;
+継続;
+}
+ break; /*他のすべてのエラーは中断されます。 */
+}
+ set_blocking(job_fds[1], 1);
+
+ if(n != 0)
     {
       (致命的なら)
         pfatal_with_name(_("ジョブサーバーへの書き込み"));

答え4

makeシステムは大丈夫かもしれませんが、これはビルドを並列に実行したときに発生する競合状態である可能性があります。

システムに問題が発生すると、並列ビルドを実行するときだけでなく、他の状況でもシステムがクラッシュしたりクラッシュしたりします。

関連情報