[インデックス 19696] ファイルの概要
このコミットは、Go言語のPlan 9向けシステムコールパッケージにおいて、時刻取得メカニズムを改善するものです。具体的には、従来の/dev/bintime
デバイスファイルからのバイナリデータ読み取りによる時刻取得から、より直接的なnsec
システムコールを利用する方式へと変更しています。これにより、コードの簡素化と効率化が図られています。また、この変更に伴い、Plan 9のアーキテクチャ固有の時刻取得関連ファイルが削除され、システムコールパッケージ全体の整理が行われています。
コミット
commit 9c3141145a4bb241bef6d3aec5d332451d6d63c7
Author: Aram Hăvărneanu <aram@mgk.ro>
Date: Wed Jul 9 12:34:06 2014 +0200
syscall: use the nsec system call instead of /dev/bintime on Plan 9
Also remove arch-specific Go files in the Plan 9 syscall package
LGTM=0intro
R=0intro, dave
CC=ality, golang-codereviews, jas, mischief, rsc
https://golang.org/cl/112720043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/9c3141145a4bb241bef6d3aec5d332451d6d63c7
元コミット内容
syscall: use the nsec system call instead of /dev/bintime on Plan 9
Also remove arch-specific Go files in the Plan 9 syscall package
LGTM=0intro
R=0intro, dave
CC=ality, golang-codereviews, jas, mischief, rsc
https://golang.org/cl/112720043
変更の背景
Go言語は、様々なオペレーティングシステム(OS)をサポートしており、その中にはPlan 9も含まれます。OS固有の機能(例えば時刻取得)にアクセスするためには、Goのsyscall
パッケージが利用されます。
従来のPlan 9におけるGoの時刻取得メカニズムは、/dev/bintime
という特殊なデバイスファイルを読み込むことで実現されていました。Plan 9の設計哲学である「全てはファイルである」に基づき、システムリソースへのアクセスはファイルI/Oを通じて行われます。/dev/bintime
は、システム時刻をバイナリ形式で提供するファイルであり、Goランタイムはこのファイルを読み込み、そのバイナリデータを解析して時刻情報を抽出していました。
しかし、このアプローチにはいくつかの課題がありました。
- 複雑性: バイナリデータを手動で解析する必要があり、コードが複雑になりがちでした。
- 効率性: ファイルI/Oは、直接的なシステムコールに比べてオーバーヘッドが大きい可能性があります。特に頻繁に時刻を取得するようなシナリオでは、パフォーマンスに影響を与える可能性がありました。
- 冗長性: 各アーキテクチャ(386, amd64など)ごとに
/dev/bintime
を扱うためのnanotime
関数が個別に実装されており、コードの重複が生じていました。
これらの課題を解決し、よりシンプルで効率的な時刻取得メカニズムを導入するために、このコミットが作成されました。Plan 9には、より高レベルな抽象化を提供するnsec
というライブラリ関数(内部的にはシステムコールを利用)が存在しており、これを利用することで上記の課題を克服できると判断されました。
前提知識の解説
Plan 9オペレーティングシステム
Plan 9 from Bell Labsは、Unixの設計思想をさらに推し進めた分散型オペレーティングシステムです。その最も特徴的な設計原則は「全てはファイルである」というもので、システムリソース(プロセス、ネットワーク接続、デバイスなど)は全てファイルシステム上のファイルとして表現され、標準的なファイルI/O操作(open
, read
, write
, close
など)を通じてアクセスされます。これにより、システムプログラミングが大幅に簡素化され、強力なツールを組み合わせることで複雑なタスクを容易に実現できます。
/dev/bintime
Plan 9において、/dev/bintime
はシステム時刻情報を提供する特殊なデバイスファイルです。このファイルは、システムクロックの値をバイナリ形式で提供します。具体的には、read
操作を行うと24バイトのデータが返され、これにはナノ秒単位の時刻(Unixエポックからの経過時間)、クロックティック、クロック周波数といった情報が8バイトのビッグエンディアン整数として含まれています。このファイルは、時刻の読み取りだけでなく、特定のバイナリデータを書き込むことでクロックの周波数調整や値のトリミングといった制御も可能です。
nsec
システムコール(ライブラリ関数)
Plan 9のlibc
ライブラリには、vlong nsec(void)
という関数が提供されています。これは、Unixエポック(1970年1月1日00:00:00 GMT)からの経過時間をナノ秒単位で返す高レベルなインターフェースです。nsec
関数は、内部的に/dev/time
や/dev/bintime
のようなデバイスファイルから時刻情報を取得する、あるいは直接的なシステムコールを呼び出すことで実装されています。この関数は、アプリケーションが現在の時刻をナノ秒精度で簡単に取得できるように設計されており、/dev/bintime
のような低レベルなファイルI/Oとバイナリ解析の複雑さを抽象化しています。
Go言語のsyscall
パッケージ
Go言語のsyscall
パッケージは、Goプログラムから基盤となるオペレーティングシステムのシステムコールにアクセスするためのインターフェースを提供します。これにより、Goの標準ライブラリでは提供されていない低レベルなOS機能や、OS固有の機能を利用することが可能になります。各OS(Linux, Windows, macOS, Plan 9など)ごとに異なるシステムコールやデータ構造に対応するため、syscall
パッケージ内にはOSやアーキテクチャ固有のファイルが存在します。
技術的詳細
このコミットの主要な変更点は、GoのPlan 9向けsyscall
パッケージにおける時刻取得ロジックの根本的な変更です。
-
/dev/bintime
からの脱却:src/pkg/syscall/syscall_plan9.go
から、/dev/bintime
を読み込んでバイナリデータを解析していたDecodeBintime
関数が削除されました。- これに伴い、
nanotime()
関数(Gettimeofday
から呼び出されていた)も削除され、直接nsec()
関数を呼び出すように変更されました。
-
nsec
システムコールの導入:src/pkg/syscall/syscall_plan9.go
に、新しいnsec()
関数が追加されました。この関数は、Syscall
関数を通じてSYS_NSEC
という新しいシステムコール定数を呼び出します。Syscall(SYS_NSEC, uintptr(unsafe.Pointer(&scratch)), 0, 0)
という形式でシステムコールが実行されます。これは、SYS_NSEC
システムコールが、時刻情報をscratch
変数に書き込むことを期待していることを示唆しています。- コメントにある
// TODO(aram): remove hack after I fix _nsec in the pc64 kernel.
は、当時のPlan 9のpc64カーネルにおける_nsec
システムコールの挙動に一時的なワークアラウンドが必要であったことを示しています。通常、システムコールは戻り値として結果を返すことが多いですが、このケースではポインタを渡して結果を書き込ませる形式と、直接戻り値として結果を返す形式の両方が考慮されています。
-
アーキテクチャ固有ファイルの削除:
src/pkg/syscall/syscall_plan9_386.go
とsrc/pkg/syscall/syscall_plan9_amd64.go
の2つのファイルが完全に削除されました。これらのファイルには、それぞれ386とamd64アーキテクチャ向けのnanotime()
関数の実装が含まれていました。nanotime()
関数は、/dev/bintime
を開いて読み込むロジックを含んでおり、アーキテクチャごとに異なるGetpagesize()
の実装も持っていました。nsec
システムコールへの移行により、アーキテクチャ固有の時刻取得ロジックが不要になったため、これらのファイルは冗長となり削除されました。これにより、コードベースが簡素化され、保守性が向上しました。
-
システムコール定数の更新:
src/pkg/syscall/zsysnum_plan9_386.go
とsrc/pkg/syscall/zsysnum_plan9_amd64.go
において、システムコール定数リストが更新されました。SYS_NSEC
という新しいシステムコール定数(386版では53、amd64版でも53)が追加されました。これは、GoランタイムがPlan 9カーネルのnsec
機能に直接アクセスするための識別子です。- また、
SYS_TSEMACQUIRE
という定数も追加されていますが、このコミットの直接的な時刻取得ロジックには関与していません。これは、関連するシステムコール番号の整理の一環である可能性があります。
この変更により、GoのPlan 9向けランタイムは、より効率的で直接的な方法でシステム時刻を取得できるようになりました。ファイルI/Oのオーバーヘッドが削減され、アーキテクチャ固有の冗長なコードが排除されたことで、全体的なパフォーマンスとコードの品質が向上しています。
コアとなるコードの変更箇所
src/pkg/syscall/syscall_plan9.go
--- a/src/pkg/syscall/syscall_plan9.go
+++ b/src/pkg/syscall/syscall_plan9.go
@@ -295,30 +295,25 @@ func NsecToTimeval(nsec int64) (tv Timeval) {
return
}
-func DecodeBintime(b []byte) (nsec int64, err error) {
- if len(b) != 8 {
- return -1, NewError("bad /dev/bintime format")
- }
- nsec = int64(b[0])<<56 |
- int64(b[1])<<48 |
- int64(b[2])<<40 |
- int64(b[3])<<32 |
- int64(b[4])<<24 |
- int64(b[5])<<16 |
- int64(b[6])<<8 |
- int64(b[7])
- return
-}
-
func nsec() int64 {
var scratch int64
r0, _, _ := Syscall(SYS_NSEC, uintptr(unsafe.Pointer(&scratch)), 0, 0)
// TODO(aram): remove hack after I fix _nsec in the pc64 kernel.
if r0 == 0 {
return scratch
}
return int64(r0)
}
func Gettimeofday(tv *Timeval) error {
- nsec, e := nanotime()
- if e != nil {
- return e
- }
+ nsec := nsec()
*tv = NsecToTimeval(nsec)
- return e
+ return nil
}
+func Getpagesize() int { return 0x1000 }
+
func Getegid() (egid int) { return -1 }
func Geteuid() (euid int) { return -1 }
func Getgid() (gid int) { return -1 }
削除されたファイル
src/pkg/syscall/syscall_plan9_386.go
src/pkg/syscall/syscall_plan9_amd64.go
src/pkg/syscall/zsysnum_plan9_386.go
および src/pkg/syscall/zsysnum_plan9_amd64.go
SYS_NSEC
およびSYS_TSEMACQUIRE
のシステムコール定数が追加されました。
コアとなるコードの解説
syscall_plan9.go
の変更
DecodeBintime
関数の削除: この関数は、/dev/bintime
から読み取った8バイトのバイナリデータをint64
のナノ秒値に変換する役割を担っていました。この関数の削除は、もはや/dev/bintime
を直接読み込む必要がなくなったことを明確に示しています。- 新しい
nsec()
関数の導入:- この関数は、
Syscall(SYS_NSEC, uintptr(unsafe.Pointer(&scratch)), 0, 0)
を呼び出すことで、Plan 9カーネルのnsec
機能に直接アクセスします。 scratch
変数は、システムコールが結果を書き込むためのメモリ領域として使用されます。r0 == 0
のチェックとint64(r0)
の戻り値は、当時のPlan 9カーネルの_nsec
システムコールの実装が、結果を直接戻り値として返す場合と、ポインタ経由で書き込む場合の両方に対応していた、あるいは過渡期であったことを示唆しています。これにより、Goランタイムはカーネルから直接ナノ秒単位の時刻を取得できるようになりました。
- この関数は、
Gettimeofday
の簡素化: 以前はnanotime()
関数を呼び出し、そのエラーを処理していましたが、新しいnsec()
関数はエラーを返さないため、Gettimeofday
のロジックが大幅に簡素化されました。これにより、時刻取得のパスがより直接的で堅牢になりました。Getpagesize()
の移動:Getpagesize()
関数がアーキテクチャ固有のファイルからsyscall_plan9.go
に移動されました。これは、この関数がアーキテクチャに依存しない共通の実装を持つようになったか、あるいはその値が固定されたことを示唆しています。
アーキテクチャ固有ファイルの削除
syscall_plan9_386.go
とsyscall_plan9_amd64.go
の削除は、このコミットの最も重要な影響の一つです。これらのファイルは、それぞれ386とamd64アーキテクチャ向けにnanotime()
関数を実装していました。nanotime()
は/dev/bintime
を開き、読み込み、解析するという複雑なロジックを含んでいました。nsec
システムコールへの移行により、このアーキテクチャ固有の複雑な時刻取得ロジックが不要になったため、これらのファイルは完全に削除されました。これにより、GoのPlan 9向けsyscall
パッケージは大幅に簡素化され、コードの重複が解消され、将来的な保守が容易になりました。
システムコール定数の更新
zsysnum_plan9_386.go
とzsysnum_plan9_amd64.go
におけるSYS_NSEC
定数の追加は、GoランタイムがPlan 9カーネルの新しい(または既存だがGoから利用されていなかった)nsec
システムコールを認識し、呼び出せるようになったことを意味します。これは、GoとPlan 9カーネル間のインターフェースが更新されたことを示しており、より効率的な時刻取得パスが確立されたことを裏付けています。
全体として、このコミットはGoのPlan 9サポートにおける時刻取得メカニズムを、より現代的で効率的、かつ保守しやすい形へと進化させた重要な変更と言えます。
関連リンク
- Go Gerrit Code Review: https://golang.org/cl/112720043
- Plan 9 from Bell Labs (Wikipedia): https://ja.wikipedia.org/wiki/Plan_9_from_Bell_Labs
参考にした情報源リンク
- Plan 9
nsec
function documentation (likely part oflibc
or system calls): https://man.cat-v.org/plan_9/2/nsec (一般的なPlan 9のmanページを参照) - Plan 9
bintime
device documentation: https://man.cat-v.org/plan_9/3/bintime (一般的なPlan 9のmanページを参照) - Web search results for "Plan 9 nsec system call vs /dev/bintime" (Google Search)
- Go言語の
syscall
パッケージに関する一般的な情報 (Go公式ドキュメントなど) - Go言語のソースコード (特に
src/pkg/syscall
ディレクトリ)