事件の原因 この事件は、社内の同僚が社内メーリンググループに質問を投稿したことから始まりました。 go1.8.3 で書かれたビジネス プログラムをしばらく実行した後、一部の goroutine がロック ForkLock を待機して停止しました。同僚は、これは go1.8.3 のバグだと考えており、go1.10 にアップグレードした後も再発しませんでした。これを理解するために、同僚が github https://github.com/golang/go/issues/26836 に問題を投稿し、再現を何度も試みましたが、うまくいきませんでした。 問題が発生したビジネスコードを参照しました。大まかな使用方法は、親プロセスが os/exec の下のコマンドを呼び出して子プロセスを開き、シェル コマンドを実行することです。次に、コマンドは golang によってカプセル化された forkExec を呼び出して子プロセスを開き、コマンドを実行します。 forkExec は ForkLock を使用します。 問題分析 ForkLock は、次のような状況を回避するために存在します。複数の goroutine が同時に exec を fork する場合、子プロセスが必要なファイル記述子のみを継承するには、親プロセスがこれらのファイル記述子を作成するときに O_CLOEXEC フラグを追加して、これらの記述子が子プロセスで閉じられ、子プロセスが必要に応じて継承する必要がある記述子を開くことができるようにする必要があります。 Linux 2.6.27 以降では、ファイルやパイプを開いたり、O_CLOEXEC を設定したりすることはアトミック操作なので、大きな問題はありません。ただし、golang ではカーネル バージョンが 2.6.23 以上である必要があります。さらに、Unix システムでは、オープンと O_CLOEXEC の設定は 2 つの操作です。 2 つの操作間でフォークが発生した場合、子プロセスは必要のないファイル記述子を継承する可能性があるため、ロックが必要になります。 forkExec のソースコードに注目してください。 問題の現象から判断すると、goroutine が forkExecPipe または forkAndExecInChild ステップでスタックし、ロックが解除されない状態になっていると考えられます。そのため、一部のゴルーチンはロックを取得できず、飢餓状態になります。 forkExecPipe*** はカーネル pipe2 を呼び出し、forkAndExecInChild*** はカーネルの clone と exec を呼び出します。 推測 pipe2 は高速なシステム コールなので、ブロックされる可能性があるシステム コールは clone と exec です。なお、この問題はgo1.10では再発しません。 forkAndExecInChild 関数における go1.8 コードと go1.9 の違いを比較します。 1.8 に行く 1.9 へ go1.9 では CLONE_VFORK と CLONE_VM が追加されました。 SIGCHILD のみを使用したクローン作成は、fork に似ていると考えられます (*** 両方とも do_fork を呼び出します)。 fork の問題は、親プロセスがより多くのメモリを占有するため、パフォーマンスが低下することです。詳細については、次のリンクを参照してください: https://bugzilla.redhat.com/show_bug.cgi?id=682922 このケースは 2011 年に提案され、今年 7 月時点でも更新中でした。この場合に反映される問題は、Linux カーネルがコピーオンライト メカニズムを導入したにもかかわらず、フォーク中にページ テーブルをコピーする必要があることです。プロセスの仮想メモリが大きいほど、コピーする必要があるページ テーブル エントリの数が多くなり、フォークが遅くなります。 Golang ディスカッション グループの誰かがテストしたところ、ヒープ サイズが 2G の場合、フォークにかかる時間はミリ秒単位まで短縮される可能性がある一方、通常は数十マイクロ秒であり、その差は数千倍にもなるとのことです。 Go1.9 では、子プロセスと親プロセスがメモリを共有できるようにするために、これら 2 つのパラメータが追加されました。これは、vfork を呼び出すのと同等です。ページテーブルをコピーする必要がないため、作成速度が速くなります。テスト結果から、数十マイクロ秒で安定しています。 したがって、go1.9 より前のバージョンで書かれたプログラムでは、プログラムのメモリ使用量が十分に大きく、プロセス作成の頻度が十分に高い場合、ForkLock は長時間待機することになるというのが妥当な推測です。 実験的デモンストレーション go1.8.3 を使用してテスト プログラムを作成し、2 コア 4G 仮想マシン (カーネル 3.10.0-693.17.1.el7.x86_64) でテストしました。 10 秒ごとに、プログラムに SIGUSR1 信号が送信され、ランタイム スタックが印刷されます。しばらく実行すると、一部の goroutine では ForkLock を取得するのにかかる時間がどんどん長くなります。以下の2枚の写真をご覧ください。 ただし、go1.9以上では上記のような状況は発生しませんでした。この結果は問題を説明するのに十分だと思います。バージョンを go1.9 以上にアップグレードすると、この問題を解決できます。 ***で書かれた vfork は、ページ テーブル エントリをコピーするフォークによって発生するパフォーマンスの問題を解決するように設計されています。ほとんどのシナリオでは、exec は fork の後に呼び出されます。 Exec はすべてのページ テーブルを削除し、新しいページ テーブルをリセットします。ページ テーブル エントリを再度コピーする必要はまったくありません。ただし、vfork の親プロセスと子プロセスはメモリを共有するため、使用時には十分に注意する必要があります。子プロセスが変数を変更すると、親プロセスに影響し、カーネルは親プロセスを一時停止して、子プロセスを先に実行させます。これらの制限により、vfork は基本的に exec を使用するシナリオに制限され、fork ほど汎用的ではありません。 vfork は注意して使用する必要があり、go1.9 が vfork とともにリリースされる前に、rawVforkSyscall が戻った後も命令は親プロセス セグメントで実行されるため、子プロセスが両者の共有スタックを破壊する可能性があるため、コードが十分に堅牢ではないという意見がありました。そのため、図に示すように、この相互影響を解決するために、rawVforkSyscall が親プロセス セグメントで何もせず、戻った後に直接戻ることを許可するコミットが提案されました。 さらに詳しく知りたい場合は、Rob Pike 氏や他のユーザーがコメントしているこのコミットのレビューを参照してください。 https://go-review.googlesource.com/c/go/+/46173 |
>>: アリババクラウドとHuyaが共同でライブストリーミング業界向けのエッジノードとクラウドエンタープライズネットワークサービスを初めて開始
最適化業界に携わる人は皆、サイト上の基礎記事の更新に細心の注意を払う必要があります。Baiduにとっ...
Cloudcone の最新のプロモーション オファーには、特別価格の VPS が 6 つあることが示...
gigsgigscloud は数日前に米国国際回線向けの新しい VPS シリーズ「LAX-GLOBA...
クラウドコンピューティングは注目の分野であり、さまざまな大手企業が参入を急いでいます。中国では、Al...
[[356547]] SaaS 企業の現状と、今後どこまで成長できるかを知るために、それほど多くの ...
コミュニケーションがなければ進歩はありません。海外のSEO仲間の記事を読んだり、彼らとコミュニケーシ...
2018年最もホットなプロジェクト:テレマーケティングロボットがあなたの参加を待っていますSEO を...
AWS は本日、大中華圏で 2 番目となる IoT 研究所を深圳に設立すると発表した。このラボは、A...
今年のブラックフライデーの VPS プロモーションは、実際にはかなりの数の人々を多かれ少なかれ不満に...
従来のソフトウェア開発とは異なり、ノーコード ツールでは開発者を雇う必要がなく、技術者以外のユーザー...
bacloud は HostCat に何度か登場しており、ウェブマスターに与える一般的な印象は、独自...
Hurricanedigital は台湾 VPS を提供しており、台湾動的 IP VPS と台湾静的...
「ゼロから始めて、社会がどんどん公正になってきていることに気づけば、まだチャンスはたくさんあると思い...
国内ホスティングプロバイダーのZorocloudは、 IDC/ISP資格を持ち、香港、日本、アメリカ...
オンラインマーケティングでは、何をするにも方向性が必要です。そうでないと、進むべき道が非常に曖昧にな...