[インデックス 18637] ファイルの概要
このコミットは、Go言語の syscall
パッケージにSolarisオペレーティングシステム(GOOS=solaris
)のサポートを追加するものです。これにより、GoプログラムがSolaris環境でシステムコールを直接利用できるようになり、OS固有の機能へのアクセスや、より低レベルな操作が可能になります。
コミット
commit 1c9861918b069b0f9a6140420355a5c91dbd069d
Author: Aram Hăvărneanu <aram@mgk.ro>
Date: Tue Feb 25 21:12:10 2014 +1100
syscall: add support for GOOS=solaris
These are only the new files, autogenerated files are in a
different CL to keep the size down.
LGTM=dave, minux.ma, jsing
R=golang-codereviews, dave, jsing, gobot, minux.ma, rsc, iant, mikioh.mikioh
CC=golang-codereviews
https://golang.org/cl/36000043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/1c9861918b069b0f9a6140420355a5c91dbd069d
元コミット内容
このコミットの目的は、Go言語の syscall
パッケージにSolarisオペレーティングシステム向けのサポートを追加することです。具体的には、Solaris固有のシステムコール、データ構造、および関連するユーティリティ関数が導入されています。コミットメッセージには「これらのファイルは新しいファイルのみであり、自動生成されたファイルはサイズを抑えるために別の変更リスト(CL)にある」と記載されており、このコミットがSolarisサポートの基盤となる手書きのコードやスクリプトに焦点を当てていることが示唆されています。
変更の背景
Go言語は、その設計思想としてクロスプラットフォーム対応を重視しています。syscall
パッケージは、Goプログラムがオペレーティングシステムの低レベルな機能(ファイル操作、プロセス管理、ネットワーク通信など)にアクセスするためのインターフェースを提供します。Goが新しいOSをサポートするためには、そのOS固有のシステムコールをGoの syscall
パッケージにマッピングし、GoのランタイムがそのOS上で正しく動作するように調整する必要があります。
このコミットが行われた2014年当時、Goは既にLinux、macOS (Darwin)、FreeBSDなどの主要なUnix系OSをサポートしていましたが、Solarisはまだ公式にサポートされていませんでした。Solarisは、特にエンタープライズ環境や特定のサーバーインフラストラクチャで利用されることがあり、Goがこれらの環境で動作するためにはSolaris固有のシステムコール実装が必要でした。このコミットは、Goの対応OSを拡大し、より多くの環境でGoプログラムが動作できるようにするための重要な一歩でした。
前提知識の解説
1. システムコール (System Call)
システムコールは、ユーザー空間で動作するプログラムが、カーネル空間で提供されるOSのサービスを要求するためのインターフェースです。ファイルI/O、プロセス生成、メモリ管理、ネットワーク通信など、OSのほとんどの機能はシステムコールを通じて提供されます。Goの syscall
パッケージは、これらのシステムコールをGo言語から呼び出すためのラッパーを提供します。
2. GOOS
と GOARCH
Goのビルドシステムでは、GOOS
(Go Operating System) と GOARCH
(Go Architecture) という環境変数を使用して、ターゲットとなるOSとCPUアーキテクチャを指定します。例えば、GOOS=linux GOARCH=amd64
はLinux x86-64環境向けのビルドを意味します。このコミットでは、GOOS=solaris
のサポートが追加されています。
3. ビルドタグ (Build Tags)
Goのソースファイルには、// +build <tag>
の形式でビルドタグを記述することができます。これにより、特定のOS、アーキテクチャ、またはその他の条件に基づいて、どのファイルをビルドに含めるかを制御できます。例えば、// +build linux
はLinuxでのみビルドされるファイルを示し、// +build darwin dragonfly freebsd linux netbsd openbsd
は複数のUnix系OSで共通して使用されるファイルを示します。このコミットでは、既存のUnix系ファイルに solaris
タグが追加され、Solaris固有のファイルには solaris
タグが付与されています。
4. Solaris OS
Solarisは、Sun Microsystems(現在はOracleが所有)によって開発されたUnix系オペレーティングシステムです。特に、その堅牢性、スケーラビリティ、およびZFSファイルシステムやDTraceなどの高度な機能で知られています。Solarisのシステムコールインターフェースは、他のBSD系やLinux系OSとは異なる部分が多く、GoがSolarisをサポートするためにはこれらの違いを吸収する専用の実装が必要でした。
5. mksyscall
および関連スクリプト
Goの syscall
パッケージでは、C言語のヘッダーファイルからシステムコールのGoラッパーコードを自動生成するためのスクリプトが使用されます。これには mksyscall.pl
(または mksyscall_unix.pl
などOS固有のもの)、mkerrors.sh
、mkall.sh
などが含まれます。これらのスクリプトは、Cの構造体定義やシステムコールプロトタイプを解析し、Goの syscall
パッケージで利用可能なGoの型定義や関数スタブを生成します。これにより、手動での記述ミスを減らし、OSのAPI変更への追従を容易にします。
6. 共有ライブラリ (Shared Library) と動的リンク
Solarisを含む多くのUnix系OSでは、共有ライブラリ(.so
ファイル)を使用して、複数のプログラム間で共通のコードを共有します。Goプログラムがこれらの共有ライブラリ内の関数を呼び出す場合、動的リンクのメカニズムを使用する必要があります。dlopen
, dlsym
, dlclose
といった関数は、実行時に共有ライブラリをロードし、その中のシンボル(関数や変数)のアドレスを取得するために使用されます。
技術的詳細
このコミットは、Goの syscall
パッケージにSolarisサポートを統合するために、以下の主要な技術的アプローチを採用しています。
-
Solaris固有のシステムコールラッパーの追加:
src/pkg/syscall/syscall_solaris.go
: Solaris固有のシステムコール(例:Pipe
,Getsockname
,Accept
,Recvmsg
,Sendmsg
など)のGoラッパー関数が定義されています。これらの関数は、Goの型とCのシステムコールインターフェース間の変換を処理します。src/pkg/syscall/syscall_solaris_amd64.go
: AMD64アーキテクチャ向けのSolaris固有のユーティリティ関数(例:Getpagesize
,TimespecToNsec
,NsecToTimeval
など)が定義されています。src/pkg/syscall/asm_solaris_amd64.s
: アセンブリ言語で記述された、Solaris AMD64向けの低レベルなシステムコール呼び出しのスタブが含まれています。Goのランタイムが直接OSのシステムコールを呼び出すためのブリッジとして機能します。
-
プロセス実行 (
exec
) のSolaris対応:src/pkg/syscall/exec_solaris.go
:forkAndExecInChild
関数がSolaris向けに実装されています。この関数は、新しいプロセスをフォークし、指定されたプログラムを実行する際の、ファイルディスクリプタの処理、chroot、ユーザー/グループIDの設定、セッションID/プロセスグループIDの設定など、OS固有の複雑なロジックを扱います。特に、Goのランタイムが提供するruntime_BeforeFork
とruntime_AfterFork
を使用して、フォーク前後のランタイムの状態を適切に管理しています。また、RawSyscall
を使用せず、手書きのシステムコール呼び出し (forkx
,execve
など) を行うことで、Goランタイムのロックやメモリ割り当てを避ける工夫がされています。
-
型定義のSolaris対応:
src/pkg/syscall/types_solaris.go
: Solarisのシステムコールで使用されるC言語の構造体(例:sockaddr_in
,sockaddr_in6
,dirent
,stat
,rusage
,timespec
,timeval
など)に対応するGoの型定義がcgo -godefs
を通じて生成されるように設定されています。これにより、GoプログラムがSolarisのシステムコールとやり取りする際に、正しいデータ構造を使用できるようになります。
-
システムコール生成スクリプトの更新:
src/pkg/syscall/mksyscall_solaris.pl
: Solaris向けのシステムコールラッパーを自動生成するためのPerlスクリプトが追加されています。このスクリプトは、//sys
または//sysnb
コメントでマークされたGoの関数プロトタイプを解析し、対応するシステムコール呼び出しのGoコードを生成します。これにより、SolarisのシステムコールAPIの変更に柔軟に対応できます。src/pkg/syscall/mkerrors.sh
: Solaris固有のエラーコードやシグナル定数をGoのコードに含めるためのスクリプトが更新され、Solarisのヘッダーファイル (sys/types.h
,sys/socket.h
など) がインクルードされるようになっています。src/pkg/syscall/mkall.sh
: 全てのシステムコール関連ファイルを生成するためのマスターシェルスクリプトが更新され、Solaris向けのmksyscall_solaris.pl
やmkerrors.sh
の実行が組み込まれています。
-
共有ライブラリの動的ロードサポート:
src/pkg/syscall/so_solaris.go
: Solarisにおける共有ライブラリ(.so
ファイル)の動的ロードを扱うためのso
(shared object) およびlazySO
(lazy shared object) 型が導入されています。これらはdlopen
,dlsym
,dlclose
といったSolarisの動的リンクAPIをGoから呼び出すためのラッパーを提供します。lazySO
は、共有ライブラリのロードやプロシージャのアドレス解決を、実際にそのプロシージャが呼び出されるまで遅延させることで、起動時のオーバーヘッドを削減します。
-
既存のUnix系ファイルの更新:
src/pkg/syscall/env_unix.go
,src/pkg/syscall/exec_unix.go
,src/pkg/syscall/sockcmsg_unix.go
,src/pkg/syscall/syscall_unix.go
,src/pkg/syscall/syscall_unix_test.go
: これらのファイルには、ビルドタグにsolaris
が追加されています。これは、SolarisがこれらのUnix系OSで共通のシステムコール実装を共有できることを示しています。
これらの変更により、GoプログラムはSolaris環境でネイティブなシステムコールを効率的かつ安全に利用できるようになり、Goのクロスプラットフォーム対応がさらに強化されました。
コアとなるコードの変更箇所
このコミットは多数のファイルにわたる変更を含んでいますが、特にSolarisサポートの核となる新規ファイルは以下の通りです。
-
src/pkg/syscall/exec_solaris.go
:forkAndExecInChild
関数がSolaris向けに実装されています。これは、新しいプロセスをフォークし、その中で指定されたコマンドを実行する際のOS固有のロジックを扱います。ファイルディスクリプタの再配置、chroot、ユーザー/グループIDの設定、セッション/プロセスグループの設定などが含まれます。
-
src/pkg/syscall/mksyscall_solaris.pl
:- Solarisのシステムコールラッパーを自動生成するためのPerlスクリプトです。Goのソースファイル内の
//sys
コメントを解析し、対応するシステムコール呼び出しのGoコードを生成します。
- Solarisのシステムコールラッパーを自動生成するためのPerlスクリプトです。Goのソースファイル内の
-
src/pkg/syscall/so_solaris.go
:- Solarisにおける共有ライブラリ(
.so
)の動的ロードを管理するためのコードが含まれています。loadSO
、FindProc
、lazySO
、lazyProc
などの型と関数が定義されており、dlopen
、dlsym
、dlclose
といったSolarisのAPIをGoから利用できるようにします。
- Solarisにおける共有ライブラリ(
-
src/pkg/syscall/syscall_solaris.go
:- Solaris固有のシステムコール(例:
Pipe
,Accept
,Recvmsg
,Sendmsg
など)のGoラッパー関数が多数定義されています。また、ParseDirent
のようなディレクトリエントリーを解析するユーティリティ関数も含まれます。
- Solaris固有のシステムコール(例:
-
src/pkg/syscall/types_solaris.go
:- Solarisのシステムコールで使用されるC言語の構造体に対応するGoの型定義が記述されています。これらは
cgo -godefs
を通じてGoの型に変換されます。
- Solarisのシステムコールで使用されるC言語の構造体に対応するGoの型定義が記述されています。これらは
コアとなるコードの解説
src/pkg/syscall/exec_solaris.go
の forkAndExecInChild
この関数は、Goプログラムが新しい外部プロセスを起動する際にSolaris上でどのように動作するかを定義します。
func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
// ... (変数宣言とfdの準備) ...
runtime_BeforeFork() // Goランタイムのフォーク前処理
r1, err1 := forkx(0x1) // FORK_NOSIGCHLD (Solaris固有のフォークシステムコール)
if err1 != 0 {
runtime_AfterFork() // Goランタイムのフォーク後処理
return 0, err1
}
if r1 != 0 {
// 親プロセス: PIDを返して終了
runtime_AfterFork()
return int(r1), 0
}
// 子プロセス: ここから先は子プロセスでの処理
// ... (Setsid, Setpgid, Chroot, User/Groups, Chdir の設定) ...
// ファイルディスクリプタの再配置とクローズオンエグゼックフラグの設定
// ... (複雑なfd操作ロジック) ...
// 最終的に execve を呼び出して新しいプログラムを実行
err1 = execve(
uintptr(unsafe.Pointer(argv0)),
uintptr(unsafe.Pointer(&argv[0])),
uintptr(unsafe.Pointer(&envv[0])))
childerror:
// エラーが発生した場合、パイプを通じて親プロセスにエラーコードを送信
write1(uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1))
for {
exit(253) // エラー終了
}
}
この関数は、Goの os/exec
パッケージの基盤となる部分で、Solarisの fork
と exec
のセマンティクスに合わせて実装されています。特に注目すべきは、runtime_BeforeFork()
と runtime_AfterFork()
の呼び出しです。これらはGoランタイムがフォークの前後で内部状態(ガベージコレクタのロックなど)を適切に処理するために必要です。また、RawSyscall
を使わず、forkx
や execve
といった手書きのシステムコールラッパーを直接呼び出すことで、Goランタイムのロックやスケジューリングの影響を受けないようにしています。これは、フォーク後の子プロセスでは、親プロセスのメモリ状態をそのまま引き継ぐため、Goランタイムの複雑な機能(ガベージコレクション、goroutineスケジューリングなど)が安全に動作しない可能性があるためです。
src/pkg/syscall/mksyscall_solaris.pl
このPerlスクリプトは、Goの syscall
パッケージのソースファイルに記述された特別なコメント (//sys
や //sysnb
) を解析し、対応するシステムコール呼び出しのGoコードを自動生成します。
# Line must be of the form
# func Open(path string, mode int, perm int) (fd int, err error)
# Split into name, in params, out params.
if(!/^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*(?:(\w*)\.)?(\w*))?$/) {
print STDERR "$ARGV:$.: malformed //sys declaration\n";
$errors = 1;
next;
}
my ($nb, $func, $in, $out, $modname, $sysname) = ($1, $2, $3, $4, $5, $6);
# ... (引数の解析とGoコードの生成ロジック) ...
# Actual call.
my $args = join(', ', @args);
my $call = "$asm($sysvarname.Addr(), $nargs, $args)";
# Assign return values.
# ... (戻り値の処理) ...
$text .= sprintf "func %s(%s)%s {\\n", $func, join(', ', @in), $out;
$text .= "\t$ret[0], $ret[1], $ret[2] := $call\\n";
# ... (エラー処理など) ...
$text .= "}\\n";
このスクリプトは、Goの syscall
パッケージの自動生成の仕組みの核心をなすものです。開発者がGoの関数プロトタイプを //sys
コメントで記述するだけで、対応する低レベルなシステムコール呼び出しコードが自動的に生成されるため、開発効率が向上し、OS間の差異を吸収する手間が軽減されます。
src/pkg/syscall/so_solaris.go
の lazySO
と lazyProc
これらの型は、Solarisにおける共有ライブラリの動的ロードを効率的に行うためのものです。
type lazySO struct {
mu sync.Mutex
so *so // non nil once SO is loaded
Name string
}
// Load loads single shared file d.Name into memory.
func (d *lazySO) Load() error {
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.so))) == nil {
d.mu.Lock()
defer d.mu.Unlock()
if d.so == nil {
so, e := loadSO(d.Name) // dlopen を呼び出す
if e != nil {
return e
}
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.so)), unsafe.Pointer(so))
}
}
return nil
}
type lazyProc struct {
mu sync.Mutex
Name string
l *lazySO
proc *proc
}
// Find searches the shared library for procedure named p.Name.
func (p *lazyProc) Find() error {
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc))) == nil {
p.mu.Lock()
defer p.mu.Unlock()
if p.proc == nil {
e := p.l.Load() // 共有ライブラリをロード
if e != nil {
return e
}
proc, e := p.l.so.FindProc(p.Name) // dlsym を呼び出す
if e != nil {
return e
}
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc)), unsafe.Pointer(proc))
}
}
return nil
}
lazySO
は共有ライブラリ自体を、lazyProc
はその中のプロシージャ(関数)を表します。これらの「lazy」な実装は、実際に共有ライブラリが使用されるまでロードやシンボル解決を遅延させることで、プログラムの起動時間を短縮し、不要なリソースの消費を避けます。これは、GoプログラムがSolarisのシステムライブラリ(例: libc.so
, libsocket.so
など)の関数を呼び出す際に利用されます。
関連リンク
- Go言語の
syscall
パッケージのドキュメント: https://pkg.go.dev/syscall - Go言語のクロスコンパイルに関するドキュメント: https://go.dev/doc/install/source#environment
- Solaris オペレーティングシステム (Wikipedia): https://ja.wikipedia.org/wiki/Solaris
参考にした情報源リンク
- Go言語のソースコード (特に
src/pkg/syscall
ディレクトリ): https://github.com/golang/go/tree/master/src/syscall - Go言語の
mksyscall
ツールに関する議論やドキュメント (GoのIssueやDesign Documentなど):- Go issue 5847: syscall: add sendfile to Solaris: https://github.com/golang/go/issues/5847 (このコミットで
sendfile
が未実装とされていることに関連) - Go issue 7402: syscall: ReadMsgUnix returns empty message on solaris: https://github.com/golang/go/issues/7402 (テストスキップの理由に関連)
- Go issue 5847: syscall: add sendfile to Solaris: https://github.com/golang/go/issues/5847 (このコミットで
- Solarisのシステムプログラミングガイド (Oracleの公式ドキュメント): https://docs.oracle.com/cd/E19253-01/html/817-0503/index.html (一般的なSolarisシステムコールの理解のため)
- Unix系OSにおける
fork
とexec
のセマンティクスに関する一般的な情報源。 - 動的リンクと
dlopen
/dlsym
/dlclose
に関する一般的な情報源。 - Goのビルドタグに関する公式ドキュメント: https://go.dev/cmd/go/#hdr-Build_constraints
cgo
のドキュメント: https://go.dev/cmd/cgo/ (GoとCの相互運用に関する理解のため)