事件の原因 この事件は、社内の同僚が社内メーリンググループに質問を投稿したことから始まりました。 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が共同でライブストリーミング業界向けのエッジノードとクラウドエンタープライズネットワークサービスを初めて開始
Pinterestの月間ユニークユーザー訪問数が9か月で1000万を突破新浪科技報、北京時間2月8日...
[[418050]]最近、アリババクラウドPAIチームとDAMOアカデミーインテリジェントコンピュー...
Nodeserv は今回、何か新しいものをもたらしました。少なくともプロモーション オプションは増え...
オープンソースデータベースの分野に、設立されてまだ5年ちょっとの若い会社があります。主力製品であるT...
9月に、登録番号のランダムチェックを通知するメールを受け取りました。ウェブサイトの登録情報は私のもの...
Googleの広報担当者はThe Vergeに対し、同社の最新の著作権侵害対策アルゴリズムは、有名な...
背景分散アーキテクチャでは、一意のシリアル番号を生成することは、特にデータベースがシャーディングを使...
2021年「中国国際サービス貿易交易会」が9月2日、国家会議センターで開幕した。今年のCIIEのテー...
誰もがローカル Web サイトをよく知っていますが、草の根の Web マスターはどのようにして優れた...
[コアヒント] Google の無料 Web デザイン ツールは現在主に広告デザインに使用されていま...
数日前、私はGoogleウェブサイトコンソールとYoudaoを通じてアンカー外部リンクを照会する記事...
「急げば速くなる」ということわざがあります。業界のウェブサイトであろうと他のサイトであろうと、短期間...
zji は、KVM 仮想化と専用リソースに基づく新製品シリーズ「Hong Kong VDS」を発表し...
Hostcram は毎年恒例のブラックフライデー プロモーションを開始しました。AMD VPS は事...
ITタイムズ記者 李東林飛パスワード、パスワード、そしてまたパスワード...現代人はパスワードなしで...