[インデックス 13436] ファイルの概要
このコミットは、Go言語のsyscall
パッケージにおいて、Linuxの386およびARMアーキテクチャ向けにGetrlimit
およびSetrlimit
システムコールが32ビット構造体を使用するように修正したものです。これにより、rlimit
関連のシステムコールが正しく動作しない問題(Go Issue #2492)が解決されました。
コミット
commit 8b7d39e7b633a2257f38ee77f7b558dbacf92f65
Author: Han-Wen Nienhuys <hanwen@google.com>
Date: Mon Jul 2 22:57:32 2012 -0700
syscall: use 32 bits structure for Getrlimit/Setrlimit on 386/ARM.
Fixes #2492
R=rsc, bradfitz
CC=golang-dev
https://golang.org/cl/6295073
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/8b7d39e7b633a2257f38ee77f7b558dbacf92f65
元コミット内容
このコミットは、Go言語のsyscall
パッケージにおけるGetrlimit
およびSetrlimit
システムコールの実装を修正するものです。特に、386(x86 32-bit)およびARMアーキテクチャにおいて、これらのシステムコールが期待通りに動作しない問題に対処しています。修正の核心は、これらのアーキテクチャでrlimit
構造体を扱う際に、64ビットではなく32ビットの構造体を使用するように変更することです。これにより、Go言語のsyscall
パッケージがLinuxカーネルのrlimit
関連のシステムコールと正しくインターフェースできるようになります。
変更の背景
この変更は、Go Issue #2492「syscall: use prlimit64, not prlimit」を修正するために行われました。この問題は、Goのtypes_linux.go
ファイルが_FILE_OFFSET_BITS 64
を定義していたことに起因します。これにより、ソースコードが64ビットのrlim_t
型でコンパイルされていました。しかし、getrlimit()
システムコールは内部的にprlimit()
(strace
ではprlimit64
として識別される)にエイリアスされており、特に32ビットアーキテクチャ(386やARM)では、期待される32ビットのrlimit
構造体ではなく、64ビットの構造体でシステムコールが呼び出されることで不整合が生じていました。
この不整合により、Getrlimit
やSetrlimit
が正しく機能せず、リソース制限の取得や設定に問題が発生していました。このコミットは、Prlimit
システムコールを優先的に使用し、それが利用できない場合にのみ従来のgetrlimit
/setrlimit
を使用するようにフォールバックロジックを導入することで、この問題を解決しています。さらに、32ビットアーキテクチャ向けに明示的に32ビットのrlimit
構造体(rlimit32
)を定義し、GoのRlimit
型との間で変換を行うことで、アーキテクチャ間の互換性を確保しています。
前提知識の解説
- システムコール (Syscall): オペレーティングシステムが提供するサービスをプログラムが利用するためのインターフェースです。ファイル操作、メモリ管理、プロセス制御など、低レベルな操作を行います。Go言語の
syscall
パッケージは、これらのシステムコールをGoプログラムから呼び出すための機能を提供します。 rlimit
(Resource Limit): プロセスが利用できるシステムリソース(例: オープンできるファイルの最大数、利用できるCPU時間、メモリ量など)を制限するための仕組みです。getrlimit
システムコールで現在のリソース制限を取得し、setrlimit
システムコールで設定します。Rlimit
構造体:rlimit
システムコールで使用される構造体で、通常はrlim_cur
(現在のソフトリミット)とrlim_max
(ハードリミット)の2つのフィールドを持ちます。これらのフィールドは、リソースの最大値を表します。prlimit
システムコール: Linuxカーネル2.6.36で導入された新しいシステムコールで、特定のプロセス(pid
で指定)のリソース制限を取得・設定できます。従来のgetrlimit
/setrlimit
は現在のプロセスのリソース制限のみを扱いました。prlimit
はより柔軟で、64ビットの値を直接扱えるprlimit64
として実装されることが多いです。- 32ビット/64ビットアーキテクチャ: CPUのレジスタ幅やメモリアドレス空間のサイズを指します。32ビットシステムではポインタや整数が32ビット(4バイト)で表現されるのに対し、64ビットシステムでは64ビット(8バイト)で表現されます。これにより、扱えるメモリ量やデータ型のサイズに違いが生じます。
ENOSYS
エラー: システムコールが実装されていない、または利用できない場合に返されるエラーコードです。このコミットでは、Prlimit
が利用できない場合にENOSYS
をチェックし、従来のgetrlimit
/setrlimit
にフォールバックするロジックが導入されています。uint32
とuint64
: Go言語における符号なし32ビット整数型と符号なし64ビット整数型です。rlimit
構造体のフィールドは、これらの型で表現されることがあります。rlimInf32
とrlimInf64
: リソース制限が無制限であることを示す定数です。32ビットシステムでは^uint32(0)
(すべてのビットが1)、64ビットシステムでは^uint64(0)
で表現されます。
技術的詳細
このコミットの主要な技術的変更点は以下の通りです。
Prlimit
の優先使用:Getrlimit
およびSetrlimit
関数内で、まずPrlimit
システムコールを呼び出すように変更されました。これは、Prlimit
がより新しいシステムコールであり、64ビットのrlimit
値を直接扱えるため、より堅牢な方法であるためです。ENOSYS
によるフォールバック:Prlimit
システムコールがENOSYS
エラーを返した場合(つまり、カーネルがPrlimit
をサポートしていない場合)、従来のgetrlimit
またはsetrlimit
システムコールにフォールバックするロジックが追加されました。これにより、古いLinuxカーネルでもGoプログラムが動作するように互換性が保たれます。- 32ビット
rlimit
構造体rlimit32
の導入: 386およびARMアーキテクチャ向けに、rlimit32
という新しい構造体が定義されました。この構造体は、Cur
とMax
フィールドをuint32
型として持ちます。これは、これらのアーキテクチャのLinuxカーネルがrlimit
システムコールで32ビットの構造体を期待するためです。 Rlimit
とrlimit32
間の変換:Getrlimit
およびSetrlimit
関数内で、Goの標準Rlimit
構造体(フィールドがuint64
)と、アーキテクチャ固有のrlimit32
構造体との間で値の変換が行われます。Getrlimit
では、rlimit32
で取得した値をRlimit
のuint64
フィールドに変換します。rlimInf32
(32ビットの無制限値)はrlimInf64
(64ビットの無制限値)に変換されます。Setrlimit
では、Rlimit
のuint64
フィールドをrlimit32
のuint32
フィールドに変換します。この際、rlimInf64
はrlimInf32
に変換され、uint32
の範囲を超える値が設定されようとした場合はEINVAL
エラーを返します。
zsyscall
ファイルの変更:zsyscall_linux_386.go
、zsyscall_linux_amd64.go
、zsyscall_linux_arm.go
ファイルから、従来のGetrlimit
とSetrlimit
の直接的なシステムコール呼び出しが削除されました。代わりに、Prlimit
のシステムコール呼び出しが追加され、386およびARM向けにはgetrlimit
とsetrlimit
という新しい内部関数が、rlimit32
構造体を使用するように定義されました。これらのzsyscall
ファイルは、Goのツールによって自動生成されるため、この変更はGoのビルドプロセスに統合されています。
この変更により、Go言語のsyscall
パッケージは、異なるアーキテクチャ(特に32ビットシステム)上でのrlimit
関連のシステムコールをより正確かつ堅牢に処理できるようになりました。
コアとなるコードの変更箇所
このコミットで変更された主要なファイルとコードスニペットは以下の通りです。
src/pkg/syscall/syscall_linux.go
:Getrlimit
とSetrlimit
の//sysnb
ディレクティブがコメントアウトされ、Prlimit
の//sys
ディレクティブが追加されました。これは、これらの関数がアーキテクチャ固有のファイルでカスタム実装されることを示唆しています。
src/pkg/syscall/syscall_linux_386.go
およびsrc/pkg/syscall/syscall_linux_arm.go
:rlimit32
構造体が定義されました。type rlimit32 struct { Cur uint32 Max uint32 }
getrlimit
とsetrlimit
の//sysnb
ディレクティブが追加され、rlimit32
構造体を使用するように指定されました。Getrlimit
関数が再実装され、Prlimit
を試行し、ENOSYS
の場合はgetrlimit
にフォールバックし、rlimit32
からRlimit
への変換を行うロジックが追加されました。func Getrlimit(resource int, rlim *Rlimit) (err error) { err = Prlimit(0, resource, rlim, nil) if err != ENOSYS { return err } rl := rlimit32{} err = getrlimit(resource, &rl) if err != nil { return } if rl.Cur == rlimInf32 { rlim.Cur = rlimInf64 } else { rlim.Cur = uint64(rl.Cur) } if rl.Max == rlimInf32 { rlim.Max = rlimInf64 } else { rlim.Max = uint64(rl.Max) } return }
Setrlimit
関数も同様に再実装され、Prlimit
を試行し、ENOSYS
の場合はsetrlimit
にフォールバックし、Rlimit
からrlimit32
への変換と値の検証を行うロジックが追加されました。func Setrlimit(resource int, rlim *Rlimit) (err error) { err = Prlimit(0, resource, nil, rlim) if err != ENOSYS { return err } rl := rlimit32{} if rlim.Cur == rlimInf64 { rl.Cur = rlimInf32 } else if rlim.Cur < uint64(rlimInf32) { rl.Cur = uint32(rlim.Cur) } else { return EINVAL } if rlim.Max == rlimInf64 { rl.Max = rlimInf32 } else if rlim.Max < uint64(rlimInf32) { rl.Max = uint32(rlim.Max) } else { return EINVAL } return setrlimit(resource, &rl) }
src/pkg/syscall/syscall_linux_amd64.go
:Getrlimit
とSetrlimit
の//sysnb
ディレクティブが追加されました。これは、amd64ではRlimit
構造体が64ビットであるため、直接システムコールを呼び出すように変更されたことを示唆しています。
src/pkg/syscall/zsyscall_linux_386.go
,src/pkg/syscall/zsyscall_linux_amd64.go
,src/pkg/syscall/zsyscall_linux_arm.go
:- 従来の
Getrlimit
とSetrlimit
の自動生成された関数が削除されました。 Prlimit
の自動生成された関数が追加されました。- 386およびARM向けには、
getrlimit
とsetrlimit
という新しい自動生成関数が、rlimit32
構造体を使用するように追加されました。
- 従来の
コアとなるコードの解説
このコミットの核心は、Go言語のsyscall
パッケージがLinuxカーネルのリソース制限(rlimit
)システムコールを扱う方法を、アーキテクチャの特性に合わせて最適化し、バグを修正した点にあります。
-
Prlimit
の導入とフォールバック: GoのGetrlimit
とSetrlimit
関数は、まず新しいprlimit
システムコール(GoではPrlimit
としてラップ)を呼び出そうとします。err = Prlimit(0, resource, rlim, nil) // Getrlimitの場合 err = Prlimit(0, resource, nil, rlim) // Setrlimitの場合 if err != ENOSYS { return err }
Prlimit
は、従来のgetrlimit
/setrlimit
よりも柔軟で、64ビットのrlimit
値を直接扱えるため、より推奨される方法です。しかし、古いLinuxカーネルではPrlimit
が利用できない場合があります。その場合、カーネルはENOSYS
(No such system call)エラーを返します。このコミットでは、このENOSYS
エラーを検知した場合に、従来のgetrlimit
/setrlimit
システムコールにフォールバックするロジックが組み込まれています。これにより、Goプログラムは幅広いLinux環境でリソース制限を適切に扱えるようになります。 -
32ビットアーキテクチャ(386/ARM)における
rlimit32
の利用: GoのRlimit
構造体は、Cur
とMax
フィールドをuint64
型として定義しています。これは64ビットシステムでは問題ありませんが、32ビットシステム(386やARM)のLinuxカーネルは、getrlimit
/setrlimit
システムコールで32ビットのrlimit
構造体(rlim_cur
とrlim_max
が32ビット)を期待します。この不一致がGo Issue #2492の原因でした。このコミットでは、この問題を解決するために、386およびARMアーキテクチャ向けに
rlimit32
という新しい構造体を導入しました。type rlimit32 struct { Cur uint32 Max uint32 }
そして、
Getrlimit
とSetrlimit
関数内で、GoのRlimit
構造体と、実際にシステムコールに渡されるrlimit32
構造体との間で、値の変換と検証が行われます。-
Getrlimit
の変換:getrlimit
システムコールからrlimit32
構造体で値を取得した後、GoのRlimit
構造体に変換します。この際、32ビットの無制限値rlimInf32
は64ビットの無制限値rlimInf64
に適切にマッピングされます。if rl.Cur == rlimInf32 { rlim.Cur = rlimInf64 } else { rlim.Cur = uint64(rl.Cur) } // Maxも同様
-
Setrlimit
の変換と検証:Setrlimit
では、GoのRlimit
構造体からrlimit32
構造体に値を変換します。ここでの重要な点は、uint64
の値をuint32
に変換する際に、値がuint32
の範囲内に収まっているかどうかの検証が行われることです。if rlim.Cur == rlimInf64 { rl.Cur = rlimInf32 } else if rlim.Cur < uint64(rlimInf32) { // uint32の最大値より小さいか rl.Cur = uint32(rlim.Cur) } else { return EINVAL // 範囲外の場合はエラー } // Maxも同様
これにより、32ビットシステムで扱えない大きなリソース制限値を設定しようとした場合に、
EINVAL
(Invalid argument)エラーが返されるようになり、不正な状態を防ぎます。
-
この一連の変更により、Go言語のsyscall
パッケージは、異なるLinuxアーキテクチャ間でのrlimit
システムコールの挙動の差異を吸収し、より堅牢で互換性の高いリソース制限管理機能を提供できるようになりました。
関連リンク
- Go Issue #2492: https://github.com/golang/go/issues/2492
- Go CL 6295073: https://golang.org/cl/6295073
参考にした情報源リンク
- Go Issue #2492のWeb検索結果: https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGVaxQ7kT6x-O35LNW18NVdbbU6dU5oULhqOumS4v8EVMLjSXJpmsxV1fcn2p6unvaYBiXwb9qqskjZ9P04hEMo82pF9O4JM4nh8S-5qiZSRFN-8EAPIviespoL6b4fZ5fC1A==
- Narkiveの関連情報: https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFXwMR4DXV-tt6k2sLBKH35R3M8du7Xbzxc4R2soOIzsl4aq47AV6Jy4mJXE6R3zTGQ6eJjkGnu0ODjwGrf9aBjPI0lw6X8Rc39yxkjQ2KBoOhkib0AdbeiE5OQvKf3whPLMpR4QyqIU3GmM4MFGvlNsn8WnFpA4XblbxVPJ7Zjx5kQ6NfGRwGFDazAeNGRUtdpXbYjduMlY053ozVw6GRbjV7EFJD2wuzcZ8YpUjQM98UA8o1-kPE=