Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 13833] ファイルの概要

このコミットは、Go言語のビルドスクリプトである run.bash において、ファイルディスクリプタ数とデータセグメントサイズに関するulimit(ユーザープロセスが利用できるリソースの上限)を適切に設定する変更を導入しています。特にNetBSDおよびOpenBSD環境でのビルドの安定性と成功率を向上させることを目的としています。

コミット

commit 329b27a5666c469212a73626a278feb49b1b0e89
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Mon Sep 17 01:11:28 2012 +0800

    run.bash: set appropriate ulimits
        mainly for NetBSD/OpenBSD.
    
    R=bradfitz, r, rsc
    CC=golang-dev
    https://golang.org/cl/6453154
---
 src/run.bash | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/run.bash b/src/run.bash
index e818e96ecc..01560fa371 100755
--- a/src/run.bash
+++ b/src/run.bash
@@ -14,6 +14,12 @@ unset GOPATH    # we disallow local import for non-local packages, if $GOROOT ha
 # no core files, please
 ulimit -c 0
 
+# Raise soft limits to hard limits for NetBSD/OpenBSD.\n+# We need at least 256 files and ~300 MB of bss.\n+# On OS X ulimit -S -n rejects \'unlimited\'.\n+[ \"$(ulimit -H -n)\" == \"unlimited\" ] || ulimit -S -n $(ulimit -H -n)\n+[ \"$(ulimit -H -d)\" == \"unlimited\" ] || ulimit -S -n $(ulimit -H -d)\n+\n # allow all.bash to avoid double-build of everything\n rebuild=true
 if [ \"$1\" = \"--no-rebuild\" ]; then

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/329b27a5666c469212a73626a278feb49b1b0e89

元コミット内容

run.bash スクリプトにおいて、適切な ulimit を設定する。これは主にNetBSDおよびOpenBSD向けである。

変更の背景

Go言語のビルドプロセス、特に大規模なプロジェクトや多数のファイルを開く必要がある場合、オペレーティングシステムがプロセスに課すリソース制限(ulimit)が問題となることがあります。特にNetBSDやOpenBSDのような一部のUNIX系OSでは、デフォルトのソフトリミットがGoのビルドに必要なリソース(特にオープンできるファイルディスクリプタの数やデータセグメントのサイズ)に対して低すぎる場合がありました。

この制限により、ビルド中に「Too many open files」エラーやメモリ関連のエラーが発生し、ビルドが失敗する可能性がありました。開発者は、これらのOS上でGoのビルドを安定して実行できるようにするために、run.bashスクリプト内で明示的にこれらのリソース制限を引き上げる必要がありました。

コミットメッセージにある「We need at least 256 files and ~300 MB of bss」という記述は、Goのビルドプロセスが最低限必要とするリソースの目安を示しており、これらのリソースが不足するとビルドが正常に完了しないことを示唆しています。また、「On OS X ulimit -S -n rejects 'unlimited'」というコメントは、macOS(OS X)環境における ulimit コマンドの挙動の差異に対応する必要があったことを示しています。macOSでは、ソフトリミットをハードリミットと同じ「unlimited」に設定しようとするとエラーになるため、このコミットではその特殊なケースも考慮した汎用的な解決策が導入されています。

前提知識の解説

ulimit とは

ulimit は、UNIX系オペレーティングシステムにおいて、ユーザープロセスが利用できるシステムリソースの上限を設定または表示するためのコマンドです。これにより、システム全体の安定性を保ちつつ、個々のプロセスが過剰なリソースを消費するのを防ぎます。

ulimit には「ソフトリミット (soft limit)」と「ハードリミット (hard limit)」の2種類があります。

  • ソフトリミット (Soft Limit): 現在適用されているリソースの上限です。ユーザーは、ハードリミットを超えない範囲で、この値を引き上げることができます。
  • ハードリミット (Hard Limit): ソフトリミットが引き上げられる最大値です。一般ユーザーはハードリミットを引き上げることはできません。スーパーユーザー(root)のみがハードリミットを変更できます。

主な ulimit の種類には以下のようなものがあります。

  • -n (file descriptors): プロセスが同時に開くことができるファイルディスクリプタの最大数。
  • -d (data segment size): プロセスのデータセグメントの最大サイズ(バイト単位)。
  • -s (stack size): プロセスのスタックサイズの最大値(キロバイト単位)。
  • -c (core file size): コアファイルの最大サイズ(ブロック単位)。

run.bash スクリプト

run.bash は、Go言語のソースコードリポジトリに含まれるシェルスクリプトで、Goのテストスイートを実行するために使用されます。Goのビルドシステムやテスト環境をセットアップし、様々なテストを実行する役割を担っています。このスクリプトは、Goの開発者が変更をテストしたり、CI/CDパイプラインでGoのビルドとテストを実行したりする際に利用されます。

ファイルディスクリプタ

ファイルディスクリプタは、UNIX系システムにおいて、開かれたファイルやソケット、パイプなどのI/Oリソースを識別するためにカーネルがプロセスに割り当てる非負の整数です。プログラムがファイルを開いたり、ネットワーク接続を確立したりするたびに、新しいファイルディスクリプタが割り当てられます。Goのビルドプロセスでは、多数のソースファイルを読み込んだり、中間ファイルを生成したり、コンパイラやリンカが内部的に多くのファイルを扱ったりするため、多くのファイルディスクリプタを必要とすることがあります。

データセグメントサイズ (bss)

データセグメントは、プログラムのグローバル変数や静的変数が格納されるメモリ領域です。特に初期化されていないグローバル変数や静的変数は「BSS (Block Started by Symbol) セグメント」に配置されます。Goのコンパイラやリンカは、大規模なプログラムをビルドする際に、多くのシンボル情報や中間データをメモリ上に保持する必要があり、その結果、データセグメントのサイズが大きくなることがあります。ulimit -d はこのデータセグメントの最大サイズを制限します。

技術的詳細

このコミットで追加された行は、ulimit コマンドを使用して、ファイルディスクリプタ数とデータセグメントサイズのソフトリミットを、対応するハードリミットまで引き上げることを目的としています。

[ "$(ulimit -H -n)" == "unlimited" ] || ulimit -S -n $(ulimit -H -n)
[ "$(ulimit -H -d)" == "unlimited" ] || ulimit -S -n $(ulimit -H -d)

それぞれのコマンドについて詳しく見ていきます。

  1. ulimit -H -n

    • -H: ハードリミットを表示します。
    • -n: オープンできるファイルディスクリプタの最大数を指定します。
    • このコマンドは、現在のユーザーのファイルディスクリプタのハードリミットを表示します。
  2. ulimit -S -n $(ulimit -H -n)

    • -S: ソフトリミットを設定します。
    • -n: オープンできるファイルディスクリプタの最大数を指定します。
    • $(ulimit -H -n): コマンド置換により、ファイルディスクリプタのハードリミットの値を取得し、それをソフトリミットとして設定します。
    • このコマンドは、ファイルディスクリプタのソフトリミットを、ハードリミットと同じ値に引き上げます。
  3. [ "$(ulimit -H -n)" == "unlimited" ] || ...

    • これはシェルスクリプトにおける条件付き実行のパターンです。
    • [ ... ] はテストコマンドで、条件を評価します。
    • "$(ulimit -H -n)" == "unlimited": ファイルディスクリプタのハードリミットが文字列 "unlimited" と等しいかどうかをチェックします。
    • ||: 論理OR演算子です。左側のコマンド([ ... ])が失敗した場合(つまり、条件が偽の場合)にのみ、右側のコマンドが実行されます。
    • この行全体は、「もしファイルディスクリプタのハードリミットが 'unlimited' でないならば、ソフトリミットをハードリミットの値に設定する」という意味になります。

同様のロジックがデータセグメントサイズ (-d) にも適用されています。

  • ulimit -H -d: データセグメントサイズのハードリミットを表示します。
  • ulimit -S -d $(ulimit -H -d): データセグメントサイズのソフトリミットを、ハードリミットと同じ値に引き上げます。
  • [ "$(ulimit -H -d)" == "unlimited" ] || ...: データセグメントサイズのハードリミットが 'unlimited' でない場合にのみ、ソフトリミットをハードリミットの値に設定します。

なぜ unlimited をチェックするのか?

コミットメッセージに「On OS X ulimit -S -n rejects 'unlimited'」とあるように、一部のシステム(特にmacOS)では、ulimit -S -n unlimited のようにソフトリミットを明示的に「unlimited」に設定しようとするとエラーになる場合があります。しかし、ulimit -H -n が「unlimited」を返す場合、それは既にリミットがない状態を意味するため、ソフトリミットをさらに引き上げる必要はありません。

この条件式 [ "$(ulimit -H -n)" == "unlimited" ] || ... を使用することで、ハードリミットが既に「unlimited」であるシステムでは ulimit -S コマンドが実行されず、エラーを回避できます。ハードリミットが具体的な数値であるシステム(例: 1024, 2048など)では、ソフトリミットがその数値まで引き上げられ、Goのビルドに必要なリソースが確保されるようになります。

この変更は、Goのビルドスクリプトが様々なUNIX系OS上でより堅牢に動作するための重要な改善です。

コアとなるコードの変更箇所

--- a/src/run.bash
+++ b/src/run.bash
@@ -14,6 +14,12 @@ unset GOPATH    # we disallow local import for non-local packages, if $GOROOT ha
 # no core files, please
 ulimit -c 0
 
+# Raise soft limits to hard limits for NetBSD/OpenBSD.\n+# We need at least 256 files and ~300 MB of bss.\n+# On OS X ulimit -S -n rejects \'unlimited\'.\n+[ \"$(ulimit -H -n)\" == \"unlimited\" ] || ulimit -S -n $(ulimit -H -n)\n+[ \"$(ulimit -H -d)\" == \"unlimited\" ] || ulimit -S -n $(ulimit -H -d)\n+\n # allow all.bash to avoid double-build of everything
 rebuild=true
 if [ \"$1\" = \"--no-rebuild\" ]; then

コアとなるコードの解説

追加された6行のコードは、run.bash スクリプトの既存の ulimit -c 0 (コアファイルの生成を無効にする) の直後に挿入されています。

  1. # Raise soft limits to hard limits for NetBSD/OpenBSD. # We need at least 256 files and ~300 MB of bss. # On OS X ulimit -S -n rejects 'unlimited'. これらの行はコメントであり、この変更の目的と背景を説明しています。

    • NetBSD/OpenBSD向けにソフトリミットをハードリミットまで引き上げる。
    • Goのビルドには最低256個のファイルディスクリプタと約300MBのBSS(データセグメント)が必要である。
    • macOSでは ulimit -S -n unlimited がエラーになるため、その考慮が必要である。
  2. [ "$(ulimit -H -n)" == "unlimited" ] || ulimit -S -n $(ulimit -H -n) この行は、ファイルディスクリプタのソフトリミットを設定します。

    • まず、ulimit -H -n を実行して、ファイルディスクリプタのハードリミットを取得します。
    • その値が "unlimited" であるかどうかをチェックします。
    • もし "unlimited" でない場合(つまり、具体的な数値である場合)、ulimit -S -n コマンドを実行し、ソフトリミットをハードリミットの値に設定します。これにより、プロセスが利用できるファイルディスクリプタの数が最大化され、ビルド中の「Too many open files」エラーを防ぎます。
  3. [ "$(ulimit -H -d)" == "unlimited" ] || ulimit -S -n $(ulimit -H -d) この行は、データセグメントサイズのソフトリミットを設定します。

    • 同様に、ulimit -H -d を実行して、データセグメントサイズのハードリミットを取得します。
    • その値が "unlimited" であるかどうかをチェックします。
    • もし "unlimited" でない場合、ulimit -S -d コマンドを実行し、ソフトリミットをハードリミットの値に設定します。これにより、Goのビルドプロセスが十分なメモリを確保できるようになり、メモリ関連のエラーを防ぎます。

これらの変更により、Goのビルドスクリプトは、特にリソース制限が厳しいNetBSDやOpenBSDのような環境でも、より安定して動作するようになります。

関連リンク

  • Go言語の公式ウェブサイト: https://go.dev/
  • Go言語のGitHubリポジトリ: https://github.com/golang/go
  • ulimit コマンドに関するmanページ (一般的な情報): man ulimit (UNIX系システムで実行)

参考にした情報源リンク

  • ulimit コマンドのドキュメントや解説記事 (例: Linux man pages, Stack Overflowなど)
  • Go言語のIssueトラッカーやメーリングリストでの関連議論 (コミットメッセージのCLリンクから辿れる可能性あり)
  • NetBSDおよびOpenBSDのドキュメント (ulimitのデフォルト値や挙動に関する情報)
  • macOS (OS X) のシェル環境における ulimit の挙動に関する情報