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

[インデックス 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ランタイムはこのファイルを読み込み、そのバイナリデータを解析して時刻情報を抽出していました。

しかし、このアプローチにはいくつかの課題がありました。

  1. 複雑性: バイナリデータを手動で解析する必要があり、コードが複雑になりがちでした。
  2. 効率性: ファイルI/Oは、直接的なシステムコールに比べてオーバーヘッドが大きい可能性があります。特に頻繁に時刻を取得するようなシナリオでは、パフォーマンスに影響を与える可能性がありました。
  3. 冗長性: 各アーキテクチャ(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パッケージにおける時刻取得ロジックの根本的な変更です。

  1. /dev/bintimeからの脱却:

    • src/pkg/syscall/syscall_plan9.goから、/dev/bintimeを読み込んでバイナリデータを解析していたDecodeBintime関数が削除されました。
    • これに伴い、nanotime()関数(Gettimeofdayから呼び出されていた)も削除され、直接nsec()関数を呼び出すように変更されました。
  2. 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システムコールの挙動に一時的なワークアラウンドが必要であったことを示しています。通常、システムコールは戻り値として結果を返すことが多いですが、このケースではポインタを渡して結果を書き込ませる形式と、直接戻り値として結果を返す形式の両方が考慮されています。
  3. アーキテクチャ固有ファイルの削除:

    • src/pkg/syscall/syscall_plan9_386.gosrc/pkg/syscall/syscall_plan9_amd64.goの2つのファイルが完全に削除されました。これらのファイルには、それぞれ386とamd64アーキテクチャ向けのnanotime()関数の実装が含まれていました。
    • nanotime()関数は、/dev/bintimeを開いて読み込むロジックを含んでおり、アーキテクチャごとに異なるGetpagesize()の実装も持っていました。
    • nsecシステムコールへの移行により、アーキテクチャ固有の時刻取得ロジックが不要になったため、これらのファイルは冗長となり削除されました。これにより、コードベースが簡素化され、保守性が向上しました。
  4. システムコール定数の更新:

    • src/pkg/syscall/zsysnum_plan9_386.gosrc/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.gosyscall_plan9_amd64.goの削除は、このコミットの最も重要な影響の一つです。これらのファイルは、それぞれ386とamd64アーキテクチャ向けにnanotime()関数を実装していました。nanotime()/dev/bintimeを開き、読み込み、解析するという複雑なロジックを含んでいました。nsecシステムコールへの移行により、このアーキテクチャ固有の複雑な時刻取得ロジックが不要になったため、これらのファイルは完全に削除されました。これにより、GoのPlan 9向けsyscallパッケージは大幅に簡素化され、コードの重複が解消され、将来的な保守が容易になりました。

システムコール定数の更新

zsysnum_plan9_386.gozsysnum_plan9_amd64.goにおけるSYS_NSEC定数の追加は、GoランタイムがPlan 9カーネルの新しい(または既存だがGoから利用されていなかった)nsecシステムコールを認識し、呼び出せるようになったことを意味します。これは、GoとPlan 9カーネル間のインターフェースが更新されたことを示しており、より効率的な時刻取得パスが確立されたことを裏付けています。

全体として、このコミットはGoのPlan 9サポートにおける時刻取得メカニズムを、より現代的で効率的、かつ保守しやすい形へと進化させた重要な変更と言えます。

関連リンク

参考にした情報源リンク

  • Plan 9 nsec function documentation (likely part of libc 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ディレクトリ)