[インデックス 18277] ファイルの概要
このコミットは、GoランタイムがSolarisオペレーティングシステムをサポートするための変更を導入しています。具体的には、GOOS=solaris環境でのビルドと実行を可能にするための、システムコール、メモリ管理、シグナルハンドリング、スレッドローカルストレージ(TLS)などの低レベルなOS固有の実装が追加されています。
コミット
commit a46b43493154bf3c59ce634ee9557a0b273de5ce
Author: Aram Hăvărneanu <aram@mgk.ro>
Date: Fri Jan 17 17:58:10 2014 +1300
runtime: add support for GOOS=solaris
R=alex.brainman, dave, jsing, gobot, rsc
CC=golang-codereviews
https://golang.org/cl/35990043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/a46b43493154bf3c59ce634ee9557a0b273de5ce
元コミット内容
runtime: add support for GOOS=solaris
変更の背景
このコミットの背景には、Go言語のクロスプラットフォーム対応の拡大があります。Goは設計当初から様々なオペレーティングシステム(OS)とアーキテクチャをサポートすることを目指しており、その一環としてSolarisへの対応が求められました。Solarisは、特にエンタープライズ環境で利用されるUNIX系のOSであり、Goアプリケーションがこれらの環境でネイティブに動作できるようにすることは、Goの採用を促進する上で重要でした。
Goランタイムは、OS固有の機能(システムコール、スレッド管理、メモリ割り当て、シグナル処理など)に深く依存しています。新しいOSをサポートするためには、これらの低レベルなインタラクションをそのOSのAPIに合わせて実装し直す必要があります。このコミットは、Solaris/AMD64環境でGoプログラムが正しく動作するために必要な、これらのランタイムレベルの変更を網羅的に導入しています。
前提知識の解説
1. Goランタイム (Go Runtime)
Goランタイムは、Goプログラムの実行を管理するGo言語の不可欠な部分です。これには、ガベージコレクタ、スケジューラ(ゴルーチンをOSスレッドにマッピング)、メモリ割り当て、チャネル操作、システムコールインターフェースなどが含まれます。Goプログラムは、OSと直接対話するのではなく、ランタイムを介してこれらの低レベルな操作を実行します。
2. GOOSとGOARCH
GOOSとGOARCHは、Goのビルドシステムで使用される環境変数です。
GOOS: ターゲットとなるオペレーティングシステム(例:linux,windows,darwin,solaris)。GOARCH: ターゲットとなるアーキテクチャ(例:amd64,arm,386)。 これらの変数を設定することで、Goコンパイラは特定のOSとアーキテクチャ向けにバイナリを生成します。このコミットでは、GOOS=solarisのサポートが追加されています。
3. システムコール (System Calls)
システムコールは、ユーザー空間のプログラムがOSカーネルのサービスを要求するためのメカニズムです。ファイルI/O、メモリ管理、プロセス制御、ネットワーク通信など、OSが提供するほとんどの機能はシステムコールを介してアクセスされます。OSごとにシステムコールのインターフェースや番号が異なるため、新しいOSをサポートするには、そのOSのシステムコールをGoランタイムから呼び出せるようにマッピングする必要があります。
4. スレッドローカルストレージ (TLS: Thread-Local Storage)
TLSは、各スレッドが独自のデータコピーを持つことを可能にするメカニズムです。Goランタイムでは、ゴルーチン(Goの軽量スレッド)のコンテキスト情報や、現在のM(OSスレッド)に関する情報などをTLSに格納することがあります。AMD64アーキテクチャでは、FSまたはGSレジスタがTLSベースアドレスを指すために使用されることが一般的ですが、OSによってその具体的な利用方法が異なります。Solarisでは、FSレジスタがTLSのアクセスに使用されます。
5. シグナルハンドリング (Signal Handling)
シグナルは、OSがプロセスに非同期イベントを通知するためのメカニズムです。例えば、プログラムのエラー(セグメンテーション違反、浮動小数点例外など)や、外部からの終了要求(Ctrl+Cなど)はシグナルとして通知されます。Goランタイムは、これらのシグナルを捕捉し、適切に処理することで、パニックの発生、プログラムの終了、またはデバッグ情報の出力などを行います。OSごとにシグナルの種類やハンドリングのAPIが異なるため、Solaris固有のシグナルハンドリングの実装が必要です。
6. mmapとメモリ管理
mmapは、ファイルやデバイスをプロセスの仮想アドレス空間にマッピングするためのシステムコールです。メモリ割り当てにも使用され、特に大きなメモリ領域を確保する際に利用されます。Goランタイムのガベージコレクタやヒープ管理は、OSからのメモリ割り当てにmmapのようなシステムコールを依存しています。Solarisにおけるmmapの挙動やフラグは他のUNIX系OSと異なる場合があるため、適切な調整が必要です。
7. sem_t (Semaphore)
セマフォは、複数のスレッド間で共有リソースへのアクセスを同期するためのプリミティブです。Goランタイムでは、ゴルーチンのスケジューリングやロック機構の一部としてセマフォが利用されることがあります。Solarisのsem_init, sem_post, sem_waitなどのセマフォAPIをGoランタイムから利用できるようにするためのラッパーが必要です。
技術的詳細
このコミットは、GoランタイムにSolaris/AMD64のサポートを追加するために、多岐にわたる変更を加えています。
1. ビルドシステム (src/cmd/dist/buildruntime.c)
src/cmd/dist/buildruntime.cに、GOOS=solarisおよびGOARCH=amd64の組み合わせに対するTLSアクセス定義が追加されました。Solaris/AMD64では、FSレジスタをTLSベースとして使用し、g(ゴルーチン構造体)とm(OSスレッド構造体)へのオフセットを定義しています。WinCallがLibCallにリネームされ、より汎用的なライブラリ呼び出しメカニズムとして扱われるようになりました。これは、Windows固有の呼び出し規約だけでなく、Solarisのような他のOSでのライブラリ呼び出しにも対応するためです。
2. アーキテクチャ固有の定義 (src/pkg/runtime/arch_amd64.h)
RuntimeGogoBytesの定義がSolaris向けに調整されました。これは、Goのスタック切り替えに関連する内部的なサイズであり、OSやアーキテクチャによって異なる場合があります。
3. アセンブリコードの変更 (src/pkg/runtime/asm_amd64.s, src/pkg/runtime/rt0_solaris_amd64.s, src/pkg/runtime/sys_solaris_amd64.s)
src/pkg/runtime/asm_amd64.sでは、TLSセットアップのスキップ条件にSolarisが追加されました。src/pkg/runtime/rt0_solaris_amd64.sは、Solaris/AMD64向けのGoプログラムのエントリポイントとなるアセンブリファイルです。_rt0_amd64_solaris関数が定義され、main関数を呼び出す前に基本的な初期化(argc,argvのセットアップ)を行います。また、runtime·issolarisというグローバル変数が定義され、GoランタイムがSolaris上で動作していることを示すフラグとして使用されます。src/pkg/runtime/sys_solaris_amd64.sは、Solaris固有のシステムコールや低レベルな関数をGoランタイムから呼び出すためのアセンブリラッパーを提供します。runtime·settls: Solaris/AMD64におけるTLSのセットアップ。runtime·miniterrno:errno変数のTLSへのポインタを設定します。これは、Cライブラリ関数が設定するエラーコードをGoランタイムが正しく取得するために重要です。runtime·nanotime1:clock_gettimeシステムコールを呼び出して高精度な時間を取得します。runtime·pipe1:pipeシステムコールを呼び出してパイプを作成します。runtime·asmsysvicall6: SysV ABI(Application Binary Interface)に準拠したCライブラリ関数を呼び出すための汎用的なラッパーです。最大6つの整数引数をサポートし、m->libcall構造体を使用して引数と戻り値をやり取りします。runtime·tstart_sysvicall: 新しいOSスレッドが開始される際のエントリポイントです。TLSのgとmポインタを設定し、Goスケジューラのruntime·mstartを呼び出します。runtime·sigtramp: シグナルハンドラが呼び出される際のエントリポイントです。シグナルコンテキストを保存し、Goランタイムのシグナルハンドラruntime·sighandlerを呼び出します。
4. CGO定義ファイル (src/pkg/runtime/defs_solaris.go, src/pkg/runtime/defs_solaris_amd64.go, src/pkg/runtime/defs_solaris_amd64.h)
src/pkg/runtime/defs_solaris.goは、SolarisのCヘッダファイルからGoの定数と型定義を生成するためのCGO入力ファイルです。sys/types.h,sys/mman.h,sys/signal.hなど、Solarisの主要なシステムヘッダがインポートされ、EINTR,PROT_READ,MAP_ANON,SA_SIGINFO,SIGHUPなどの定数や、SemT,Sigaltstack,Ucontextなどの構造体がGoの型として定義されます。src/pkg/runtime/defs_solaris_amd64.goは、AMD64アーキテクチャ固有のレジスタ定義(REG_RAX,REG_RSPなど)をGoの定数として定義します。src/pkg/runtime/defs_solaris_amd64.hは、上記の.goファイルからcgo -cdefsツールによって生成されるCヘッダファイルです。GoランタイムのCコードからSolarisの定数や構造体定義にアクセスするために使用されます。
5. 環境変数とビルドタグ (src/pkg/runtime/env_posix.c, src/pkg/runtime/netpoll.goc, src/pkg/runtime/lock_sema.c, src/pkg/runtime/signal_amd664.c, src/pkg/runtime/signal_unix.c)
- 既存のファイルに
+build solarisタグが追加され、Solaris環境でこれらのファイルがビルドされるように設定されました。これにより、Goランタイムの共通部分がSolarisでも利用できるようになります。
6. メモリ管理 (src/pkg/runtime/mem_solaris.c)
src/pkg/runtime/mem_solaris.cは、Solaris固有のメモリ管理関数を実装しています。runtime·SysAlloc:mmapを使用してメモリを割り当てます。runtime·SysFree:munmapを使用してメモリを解放します。runtime·SysReserve: メモリ領域を予約します。64ビットシステムでは、mmapのPROT_NONEフラグを使用して予約を行います。runtime·SysMap: 予約されたメモリ領域を読み書き可能にします。
7. OS固有のランタイム実装 (src/pkg/runtime/os_solaris.c, src/pkg/runtime/os_solaris.h)
src/pkg/runtime/os_solaris.cは、Solaris上でGoランタイムが動作するために必要な主要なOS固有の関数を実装しています。libc·___errnoなどのCライブラリ関数の動的インポート(#pragma dynimport)が多数追加されています。これにより、GoランタイムはSolarisの標準Cライブラリ関数を呼び出すことができます。runtime·sysvicall6: SysV ABIに準拠したCライブラリ関数を呼び出すためのGo側のラッパーです。runtime·osinit: OS固有の初期化を行います。CPU数を取得するgetncpuを呼び出します。runtime·newosproc: 新しいOSスレッドを作成します。pthread_createを使用してスレッドを生成し、そのスレッドでGoランタイムのruntime·tstart_sysvicallが実行されるように設定します。runtime·get_random_data:/dev/urandomから乱数を取得します。runtime·mpreinit,runtime·minit,runtime·unminit: M(OSスレッド)の初期化と終了に関連する関数です。特にminitでは、シグナルハンドリングの初期化が行われます。runtime·sigpanic: シグナルによって発生したパニックを処理します。SIGBUS,SIGSEGV,SIGFPEなどのシグナルに対応するパニックメッセージを生成します。runtime·memlimit: プロセスが利用できるメモリの上限を取得します。runtime·setsig,runtime·getsig,runtime·signalstack: シグナルハンドラの設定、取得、および代替シグナルスタックの設定を行います。runtime·semacreate,runtime·semasleep,runtime·semawakeup: セマフォの作成、待機、通知を実装します。これらはSolarisのsem_init,sem_wait,sem_postを呼び出します。runtime·close,runtime·exit,runtime·getcontext,runtime·getrlimit,runtime·mmap,runtime·munmap,runtime·nanotime,runtime·open,runtime·pthread_attr_destroy,runtime·pthread_attr_getstack,runtime·pthread_attr_init,runtime·pthread_attr_setdetachstate,runtime·pthread_attr_setstack,runtime·pthread_create,runtime·raise,runtime·read,runtime·sem_init,runtime·sem_post,runtime·sem_reltimedwait_np,runtime·sem_wait,runtime·setitimer,runtime·sigaction,runtime·sigaltstack,runtime·sigprocmask,runtime·sysconf,runtime·usleep,runtime·write,runtime·osyield: これらは、Solarisの対応するシステムコールやCライブラリ関数をGoランタイムから呼び出すためのラッパー関数です。
src/pkg/runtime/os_solaris.hは、Solaris固有の定数(SS_DISABLE,SIG_BLOCKなど)や、Rlimit構造体、runtime·sysvicall6のプロトタイプ宣言などを含んでいます。
8. プロセスとスケジューリング (src/pkg/runtime/proc.c)
runtime·allocm関数で、Solaris環境でもpthread_createがスタックを作成することが考慮され、mp->g0の割り当てロジックが更新されました。
9. ランタイム共通定義 (src/pkg/runtime/runtime.c, src/pkg/runtime/runtime.h)
runtime.cにint32 runtime·issolaris;が追加され、Solaris環境であるかどうかのフラグが導入されました。runtime.hでは、WinCall構造体がLibCallにリネームされ、より汎用的なライブラリ呼び出しのコンテキストを保持するように変更されました。また、M構造体にSolaris固有のフィールド(perrno,libcall,ts,scratch)が追加され、低レベルなOS呼び出しに必要なデータがMごとに保持されるようになりました。GOOS_solarisが定義されている場合にSolaris = 1となる列挙型が追加されました。runtime·netpollarmreadとruntime·netpollarmwriteのプロトタイプが追加され、Solarisのネットワークポーリングメカニズムに対応するための関数が用意されました。
10. シグナル定義 (src/pkg/runtime/signal_solaris_amd64.h, src/pkg/runtime/signals_solaris.h)
src/pkg/runtime/signal_solaris_amd64.hは、Solaris/AMD64のシグナルコンテキスト(Ucontext)からレジスタ値を取得するためのマクロ(SIG_RAX,SIG_RIPなど)を定義しています。src/pkg/runtime/signals_solaris.hは、Solarisのシグナル番号とそれに対応するGoランタイムのシグナル処理フラグ(SigNotify,SigKill,SigThrow,SigPanic,SigDefault)および説明を定義したSigTab配列を含んでいます。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は、主に以下のファイル群に集約されます。これらはSolaris上でGoランタイムが動作するための基盤を提供します。
src/pkg/runtime/os_solaris.c: Solaris固有のシステムコールラッパー、スレッド管理、シグナルハンドリング、メモリ管理など、GoランタイムとSolarisカーネル間の主要なインタフェースを実装しています。このファイルは、GoランタイムがSolaris環境で動作するために不可欠な、OS固有の低レベルな詳細をカプセル化しています。src/pkg/runtime/sys_solaris_amd64.s: Solaris/AMD64アーキテクチャに特化したアセンブリコードが含まれており、TLSのセットアップ、errnoの処理、高精度タイマーの取得、そしてSysV ABIに準拠したCライブラリ関数の呼び出し規約の処理など、非常に低レベルな操作を扱います。GoランタイムがSolarisのCライブラリと効率的に連携するために重要です。src/pkg/runtime/defs_solaris.goおよびsrc/pkg/runtime/defs_solaris_amd64.go: これらのファイルは、SolarisのCヘッダファイルからGoの定数と型定義を生成するためのCGO入力ファイルです。Solarisのシステムコールやデータ構造をGoランタイムが理解し、利用できるようにするための基盤となります。src/pkg/runtime/runtime.h:M構造体(OSスレッドを表すGoランタイムの内部構造体)にSolaris固有のフィールドが追加された点が重要です。これにより、各OSスレッドがSolaris環境で必要なコンテキスト情報(errnoポインタ、ライブラリ呼び出しバッファなど)を保持できるようになります。
これらのファイルは連携して、GoランタイムがSolarisのシステムリソースを効率的かつ安全に利用できるようにするための、OSとアーキテクチャに特化した抽象化レイヤーを形成しています。
コアとなるコードの解説
src/pkg/runtime/os_solaris.c の主要な機能
- システムコールラッパー:
runtime·sysvicall6関数は、SolarisのCライブラリ関数を呼び出すための汎用的なメカニズムを提供します。これは、Goランタイムが直接システムコールを発行するのではなく、Solarisの標準Cライブラリを介してOSサービスにアクセスするアプローチを取っていることを示しています。これにより、GoランタイムはSolarisのシステムコールインターフェースの複雑さを直接扱う必要がなくなります。 - スレッド管理:
runtime·newosproc関数は、pthread_createを呼び出して新しいOSスレッドを作成します。GoのM(OSスレッド)とG(ゴルーチン)のモデルにおいて、MはOSスレッドにマッピングされるため、OSスレッドの作成と管理はランタイムの重要な役割です。 - シグナルハンドリング:
runtime·setsigやruntime·sigpanicなどの関数は、Solarisのシグナルメカニズムと連携し、Goプログラムがシグナルを適切に処理できるようにします。特にsigpanicは、セグメンテーション違反や浮動小数点例外などの致命的なシグナルが発生した際に、Goのパニックメカニズムに変換する役割を担います。 - メモリ管理:
runtime·SysAlloc,runtime·SysFree,runtime·SysReserve,runtime·SysMapは、SolarisのmmapおよびmunmapシステムコールをGoランタイムのメモリ管理層に統合します。これにより、GoのガベージコレクタやヒープがSolaris上で正しく動作するための基盤が提供されます。 - セマフォ:
runtime·semacreate,runtime·semasleep,runtime·semawakeupは、SolarisのPOSIXセマフォAPI(sem_init,sem_wait,sem_post)をGoランタイムの同期プリミティブとして利用できるようにします。これは、ゴルーチンのスケジューリングやロックの実装に不可欠です。
src/pkg/runtime/sys_solaris_amd64.s の主要な機能
- TLSアクセス:
get_tlsマクロは、AMD64アーキテクチャのFSレジスタを利用してTLSにアクセスします。SolarisではFSがTLSベースとして使用されるため、このマクロはGoランタイムが現在のゴルーチン(g)やOSスレッド(m)のコンテキストに効率的にアクセスするために重要です。 runtime·asmsysvicall6: このアセンブリ関数は、GoランタイムからCライブラリ関数を呼び出す際のSysV ABIに準拠した呼び出し規約を処理します。引数を適切なレジスタに配置し、C関数の戻り値をGoランタイムが解釈できる形式で取得します。これは、GoとCの相互運用性において非常に重要な役割を果たします。runtime·sigtramp: シグナルが発生した際にOSによって呼び出されるアセンブリ関数です。シグナルコンテキスト(レジスタの状態など)を保存し、GoランタイムのC言語で書かれたシグナルハンドラ(runtime·sighandler)を呼び出します。これにより、Goランタイムはシグナルを捕捉し、Goのランタイム環境内で処理することができます。
src/pkg/runtime/defs_solaris.go および src/pkg/runtime/defs_solaris_amd64.go
これらのファイルは、SolarisのCヘッダファイルで定義されている定数や構造体のレイアウトをGoのコードに「翻訳」する役割を担います。例えば、EINTRやSIGSEGVのようなエラーコードやシグナル番号、UcontextやSiginfoのようなシグナルコンテキスト構造体のフィールドオフセットなどが定義されます。これにより、GoランタイムのCコードがSolarisのシステムAPIと正しくインタラクトできるようになります。
src/pkg/runtime/runtime.h の M 構造体への追加
M構造体へのperrno, libcall, ts, scratchフィールドの追加は、各OSスレッドがSolaris固有の操作に必要な状態を保持できるようにするために重要です。
perrno: Cライブラリ関数が設定するerrno変数のポインタを保持し、GoランタイムがC関数のエラーコードを正確に取得できるようにします。libcall:runtime·sysvicall6のような汎用的なライブラリ呼び出し関数が、呼び出し先の関数ポインタ、引数の数、引数リスト、戻り値などを一時的に格納するために使用します。ts(Timespec) とscratch: 低レベルなシステムコールやライブラリ呼び出しの際に、一時的なデータ(タイムアウト値や引数バッファなど)をスタックではなくM構造体内に保持することで、スタックオーバーフローのリスクを軽減し、アセンブリコードからのアクセスを容易にします。
これらの変更は、GoランタイムがSolarisの低レベルなOSインターフェースとシームレスに連携し、GoプログラムがSolaris環境で安定して動作するための基盤を構築しています。
関連リンク
- Go言語公式ドキュメント: https://golang.org/doc/
- Goのクロスコンパイルに関するドキュメント: https://go.dev/doc/install/source#environment
- Solaris オペレーティングシステム: https://www.oracle.com/solaris/
参考にした情報源リンク
- Goのコミット履歴 (GitHub): https://github.com/golang/go/commits/master
- Goのコードレビューシステム (Gerrit): https://go-review.googlesource.com/
- AMD64 System V Application Binary Interface: https://refspecs.linuxfoundation.org/AMD64-ABI-0.99.pdf
- Solaris man pages (例:
mmap(2),sigaction(2),sem_init(3C)): Oracle Solarisの公式ドキュメントやオンラインマニュアルページ - Go runtime source code: https://github.com/golang/go/tree/master/src/runtime
- Go issue tracker: https://github.com/golang/go/issues
- Go mailing lists (golang-nuts, golang-dev): https://groups.google.com/g/golang-nuts, https://groups.google.com/g/golang-dev
- Stack Overflowや技術ブログ記事 (Go runtime, Solaris internals, system programming)