[インデックス 10441] ファイルの概要
このコミットは、Go言語のランタイムにおいて、Plan 9オペレーティングシステム向けに高精度な時間取得機能である nanotime
を追加するものです。また、Plan 9のシステムコール利用を改善し、より適切なポジショナルI/O関数 (pread
, pwrite
) を導入しています。
コミット
- コミットハッシュ:
4a2d30e13bf6f52e51c347d562871648bf5ef3bb
- 作者: Anthony Martin ality@pbrane.org
- コミット日時: 2011年11月17日 木曜日 22:09:28 -0500
- コミットメッセージ:
runtime: add nanotime for Plan 9 R=paulzhol, rsc, dave, rminnich CC=golang-dev https://golang.org/cl/5327063
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/4a2d30e13bf6f52e51c347d562871648bf5ef3bb
元コミット内容
commit 4a2d30e13bf6f52e51c347d562871648bf5ef3bb
Author: Anthony Martin <ality@pbrane.org>
Date: Thu Nov 17 22:09:28 2011 -0500
runtime: add nanotime for Plan 9
R=paulzhol, rsc, dave, rminnich
CC=golang-dev
https://golang.org/cl/5327063
---
src/pkg/runtime/plan9/386/signal.c | 6 ------
src/pkg/runtime/plan9/386/sys.s | 18 ++++++++---------\n src/pkg/runtime/plan9/os.h | 13 ++++++++++---\n src/pkg/runtime/plan9/thread.c | 40 ++++++++++++++++++++++++++++++++++++++\n 4 files changed, 58 insertions(+), 19 deletions(-)
diff --git a/src/pkg/runtime/plan9/386/signal.c b/src/pkg/runtime/plan9/386/signal.c
index 77e40d35a9..c0b759c713 100644
--- a/src/pkg/runtime/plan9/386/signal.c
+++ b/src/pkg/runtime/plan9/386/signal.c
@@ -4,12 +4,6 @@
#include "runtime.h"
-int64
-runtime·nanotime(void)
-{
- // Won\'t compile.
-}
-
String
runtime·signame(int32)
{
diff --git a/src/pkg/runtime/plan9/386/sys.s b/src/pkg/runtime/plan9/386/sys.s
index a15362ff70..97be276be6 100644
--- a/src/pkg/runtime/plan9/386/sys.s
+++ b/src/pkg/runtime/plan9/386/sys.s
@@ -14,16 +14,14 @@ TEXT runtime·open(SB),7,$0
INT $64
RET
-// TODO(ality): remove use of deprecated system calls
--
-TEXT runtime·read(SB),7,$0
- MOVL $15, AX
- INT $64
-+TEXT runtime·pread(SB),7,$0
-+ MOVL $50, AX
-+ INT $64
RET
-TEXT runtime·write(SB),7,$0
- MOVL $20, AX
- INT $64
-+TEXT runtime·pwrite(SB),7,$0
-+ MOVL $51, AX
-+ INT $64
RET
TEXT runtime·close(SB),7,$0
@@ -90,9 +88,9 @@ TEXT runtime·rfork(SB),7,$0
MOVL 0(BX), BX
// more paranoia; check that stack splitting code works
-- PUSHAL
-+ PUSHL SI
CALL\truntime·emptyfunc(SB)\n- POPAL
-+ POPL SI
\t
CALL\tSI\t// fn()\n CALL\truntime·exit(SB)\ndiff --git a/src/pkg/runtime/plan9/os.h b/src/pkg/runtime/plan9/os.h
index dcbdab25a9..b7b8383494 100644
--- a/src/pkg/runtime/plan9/os.h
+++ b/src/pkg/runtime/plan9/os.h
@@ -4,6 +4,8 @@
// Plan 9-specific system calls
int32 runtime·open(uint8 *file, int32 mode);\n+int32 runtime·pread(int32 fd, void *buf, int32 nbytes, int64 offset);\n+int32 runtime·pwrite(int32 fd, void *buf, int32 nbytes, int64 offset);\n int32 runtime·read(int32 fd, void *buf, int32 nbytes);\n int32 runtime·close(int32 fd);\n void runtime·exits(int8* msg);\n@@ -16,9 +18,14 @@ int32 runtime·plan9_semrelease(uint32 *addr, int32 count);\n /* open */\n enum\n {\n-\tOREAD = 0,\n-\tOWRITE = 1,\n-\tORDWR = 2\n+\tOREAD = 0,\n+\tOWRITE = 1,\n+\tORDWR = 2,\n+\tOEXEC = 3,\n+\tOTRUNC = 16,\n+\tOCEXEC = 32,\n+\tORCLOSE = 64,\n+\tOEXCL = 0x1000\n };\n \n /* rfork */\ndiff --git a/src/pkg/runtime/plan9/thread.c b/src/pkg/runtime/plan9/thread.c
index 8ad06ca1e4..87ea8a2363 100644
--- a/src/pkg/runtime/plan9/thread.c
+++ b/src/pkg/runtime/plan9/thread.c
@@ -69,6 +69,34 @@ runtime·usleep(uint32 µs)\n runtime·sleep(ms);\n }\n \n+int64\n+runtime·nanotime(void)\n+{\n+\tstatic int32 fd = -1;\n+\tbyte b[8];\n+\tuint32 hi, lo;\n+\n+\t// As long as all goroutines share the same file\n+\t// descriptor table we can get away with using\n+\t// just a static fd. Without a lock the file can\n+\t// be opened twice but that\'s okay.\n+\t//\n+\t// Using /dev/bintime gives us a latency on the\n+\t// order of ten microseconds between two calls.\n+\t//\n+\t// The naïve implementation (without the cached\n+\t// file descriptor) is roughly four times slower\n+\t// in 9vx on a 2.16 GHz Intel Core 2 Duo.\n+\t\n+\tif(fd < 0 && (fd = runtime·open((byte*)\"/dev/bintime\", OREAD|OCEXEC)) < 0)\n+\t\treturn 0;\n+\tif(runtime·pread(fd, b, sizeof b, 0) != sizeof b)\n+\t\treturn 0;\n+\thi = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];\n+\tlo = b[4]<<24 | b[5]<<16 | b[6]<<8 | b[7];\n+\treturn (int64)hi<<32 | (int64)lo;\n+}\n+\n extern Tos *_tos;\n void\n runtime·exit(int32)\n@@ -183,3 +211,15 @@ void\n runtime·sigpanic(void)\n {\n }\n+\n+int32\n+runtime·read(int32 fd, void *buf, int32 nbytes)\n+{\n+\treturn runtime·pread(fd, buf, nbytes, -1LL);\n+}\n+\n+int32\n+runtime·write(int32 fd, void *buf, int32 nbytes)\n+{\n+\treturn runtime·pwrite(fd, buf, nbytes, -1LL);\n+}\n```
## 変更の背景
このコミットの主な目的は、Go言語のランタイムがPlan 9オペレーティングシステム上で動作する際に、高精度な時間情報(ナノ秒単位)を取得する機能 (`nanotime`) を提供することです。
従来のGoランタイムは、特定のOS環境下で `nanotime` のような高精度タイマー機能が不足している場合がありました。特にPlan 9のようなユニークな設計を持つOSでは、一般的なUnix系システムとは異なるアプローチが必要となります。このコミット以前は、`src/pkg/runtime/plan9/386/signal.c` に `runtime·nanotime` の宣言がありましたが、これはコンパイルエラーになるダミーの実装でした。
また、このコミットでは、Plan 9におけるファイルI/Oのシステムコール利用方法も改善されています。従来の `read` および `write` システムコールは、ファイルポインタの現在位置に依存するものでしたが、Plan 9では `pread` および `pwrite` といったポジショナルI/O(オフセットを指定して読み書きする)がより一般的で推奨されるアプローチです。この変更は、より堅牢で効率的なI/O操作を実現し、将来的な互換性やパフォーマンスの向上に寄与します。
さらに、アセンブリコードにおけるスタック操作の最適化も行われており、これはランタイムの安定性と効率性を高めるための一般的な改善と見られます。
## 前提知識の解説
### Go Runtime (Goランタイム)
Go言語は、ガベージコレクション、スケジューラ、メモリ管理など、プログラムの実行をサポートする「ランタイム」を内蔵しています。このランタイムは、Goプログラムが様々なオペレーティングシステム上で効率的かつ安全に動作するために不可欠な部分です。OS固有の機能(システムコール、スレッド管理、時間取得など)は、ランタイムのOS固有部分で実装されます。
### Plan 9 (プラン・ナイン)
Plan 9 from Bell Labsは、ベル研究所で開発された分散オペレーティングシステムです。Unixの設計思想をさらに推し進め、すべてのリソース(ファイル、デバイス、ネットワーク接続など)をファイルとして表現し、標準的なファイルシステムインターフェースを通じてアクセスするという特徴的な設計を持っています。
* **ファイルシステム中心主義**: Plan 9では、プリンタ、ネットワーク接続、プロセス、さらには時間情報なども `/dev` ディレクトリ以下の特殊なファイルとして扱われます。
* **`/dev/bintime`**: Plan 9において、高精度な時間情報を提供する特殊なデバイスファイルです。このファイルを読み取ることで、システムはナノ秒単位の時刻を取得できます。通常、このファイルは8バイトのバイナリデータを提供し、上位4バイトが秒、下位4バイトがナノ秒を表すといった形式で時間情報を含んでいます。
* **システムコール**: Plan 9のシステムコールは、Unix系システムと似ていますが、細部で異なる点が多くあります。特にファイルI/Oにおいては、ポジショナルI/Oが重視されます。
### `nanotime` (ナノタイム)
`nanotime` は、ナノ秒単位の精度で現在のシステム時刻を取得する関数です。プログラムのパフォーマンス測定、イベントの正確なタイミング記録、高精度なタイマーの実装など、多くの低レベルな処理で不可欠な機能です。一般的なOSでは、`clock_gettime` (POSIX) や `QueryPerformanceCounter` (Windows) など、OS固有のAPIを通じて提供されます。
### `read`/`write` vs `pread`/`pwrite` (リード/ライト vs ピーリード/ピーライト)
* **`read(fd, buf, nbytes)` / `write(fd, buf, nbytes)`**: これらの関数は、指定されたファイルディスクリプタ `fd` から `nbytes` 分のデータを `buf` に読み込む、または `buf` から `nbytes` 分のデータを `fd` に書き込むためのものです。これらの操作は、ファイルディスクリプタに関連付けられた内部のファイルポインタの現在位置から開始され、操作後にファイルポインタが更新されます。
* **`pread(fd, buf, nbytes, offset)` / `pwrite(fd, buf, nbytes, offset)`**: これらは「ポジショナルI/O」関数と呼ばれます。`read`/`write` とは異なり、`offset` 引数で明示的に読み書きを開始するファイル内の位置を指定できます。この操作は、ファイルディスクリプタの内部ファイルポインタを更新しません。これにより、複数のスレッドやプロセスが同じファイルディスクリプタを共有している場合でも、ファイルポインタの競合を気にすることなく、特定の位置に安全にアクセスできます。Plan 9では、このポジショナルI/Oがファイル操作の基本的な形式として推奨されています。
### アセンブリ言語 (x86)
* **`PUSHAL` / `POPAL`**: x86アーキテクチャにおけるアセンブリ命令で、すべての汎用レジスタ(AX, CX, DX, BX, SP, BP, SI, DI)をスタックにプッシュ/ポップします。これは、関数呼び出し前後でレジスタの状態を保存・復元するためによく使われます。
* **`PUSHL SI` / `POPL SI`**: 特定のレジスタ(この場合は `SI` レジスタ)のみをスタックにプッシュ/ポップする命令です。`PUSHAL`/`POPAL` よりも効率的で、必要なレジスタのみを保存・復元する場合に用いられます。
## 技術的詳細
このコミットは、GoランタイムのPlan 9向け実装において、以下の主要な技術的変更を導入しています。
1. **`runtime·nanotime` の実装**:
* `src/pkg/runtime/plan9/thread.c` に `runtime·nanotime` 関数が追加されました。
* この関数は、Plan 9の特殊なデバイスファイルである `/dev/bintime` から時間情報を読み取ることで、ナノ秒単位の時刻を取得します。
* ファイルディスクリプタ (`fd`) は `static` 変数としてキャッシュされます。これにより、`nanotime` が複数回呼び出されても、毎回 `/dev/bintime` を開くオーバーヘッドを避けることができます。ファイルディスクリプタテーブルがゴルーチン間で共有されることを前提としています。
* `/dev/bintime` からは8バイトのバイナリデータが読み込まれます。この8バイトは、上位4バイトと下位4バイトに分割され、それぞれ `hi` (上位32ビット) と `lo` (下位32ビット) として解釈されます。
* 最終的に、`(int64)hi<<32 | (int64)lo` の演算によって、これら2つの32ビット値が結合され、64ビットのナノ秒単位の時刻が `int64` 型で返されます。これは、Plan 9の `/dev/bintime` が提供する時間情報の一般的な形式です。
* コメントには、この実装が「10マイクロ秒オーダーのレイテンシ」を提供し、ファイルディスクリプタをキャッシュしない「素朴な実装」と比較して「約4倍高速」であることが記されています。
2. **ポジショナルI/O (`pread`, `pwrite`) の導入と利用**:
* `src/pkg/runtime/plan9/os.h` に `runtime·pread` と `runtime·pwrite` の関数プロトタイプが追加されました。
* `src/pkg/runtime/plan9/386/sys.s` では、従来の `runtime·read` と `runtime·write` のアセンブリ実装が削除され、代わりに `runtime·pread` (システムコール番号 `50`) と `runtime·pwrite` (システムコール番号 `51`) のアセンブリスタブが追加されました。これは、Plan 9のシステムコールインターフェースに合わせた変更です。
* `src/pkg/runtime/plan9/thread.c` の末尾に、Goランタイムの `runtime·read` と `runtime·write` 関数が再実装されました。これらの関数は、内部的に新しく導入された `runtime·pread` および `runtime·pwrite` を呼び出します。オフセットとして `-1LL` (Goの `int64` 型で -1) を渡すことで、従来の `read`/`write` と同様に、ファイルポインタの現在位置からの読み書きとして機能させます。Plan 9の `pread`/`pwrite` は、オフセットに -1 を指定することで、ファイルポインタの現在位置を使用する動作をエミュレートできる場合があります。
3. **`open` フラグの拡張**:
* `src/pkg/runtime/plan9/os.h` の `open` フラグ定義に、`OEXEC`, `OTRUNC`, `OCEXEC`, `ORCLOSE`, `OEXCL` が追加されました。これらはPlan 9におけるファイルオープン時の標準的なフラグであり、より多様なファイル操作のニーズに対応できるようになります。
* `OEXEC`: 実行可能ファイルとして開く。
* `OTRUNC`: ファイルをオープン時に切り詰める(内容を空にする)。
* `OCEXEC`: `exec` システムコール時にファイルディスクリプタをクローズする。
* `ORCLOSE`: ファイルディスクリプタがクローズされたときにファイルを削除する。
* `OEXCL`: ファイルが既に存在する場合にオープンを失敗させる(排他的オープン)。
4. **アセンブリコードの最適化**:
* `src/pkg/runtime/plan9/386/sys.s` の `runtime·rfork` 関数内で、スタック操作の命令が `PUSHAL`/`POPAL` から `PUSHL SI`/`POPL SI` に変更されました。これは、すべてのレジスタを保存・復元するのではなく、`SI` レジスタのみを保存・復元することで、スタック操作のオーバーヘッドを減らし、パフォーマンスを向上させるための最適化です。コメントにある「more paranoia; check that stack splitting code works」は、スタック分割(Goのgoroutineスケジューラがスタックを動的に拡張する機能)が正しく機能しているかを確認するためのテストコードの一部であり、この変更がそのテストの文脈で行われたことを示唆しています。
## コアとなるコードの変更箇所
このコミットによる主要なコード変更は以下のファイルに集中しています。
* `src/pkg/runtime/plan9/386/signal.c`:
* `runtime·nanotime` のダミー実装が削除されました。
* `src/pkg/runtime/plan9/386/sys.s`:
* `runtime·read` および `runtime·write` のアセンブリ実装が削除され、代わりに `runtime·pread` (システムコール番号 `50`) と `runtime·pwrite` (システムコール番号 `51`) のアセンブリ実装が追加されました。
* `runtime·rfork` 内のスタック操作が `PUSHAL`/`POPAL` から `PUSHL SI`/`POPL SI` に変更されました。
* `src/pkg/runtime/plan9/os.h`:
* `runtime·pread` と `runtime·pwrite` の関数プロトタイプが追加されました。
* `open` システムコールで使用される新しいフラグ (`OEXEC`, `OTRUNC`, `OCEXEC`, `ORCLOSE`, `OEXCL`) が `enum` 定義に追加されました。
* `src/pkg/runtime/plan9/thread.c`:
* `runtime·nanotime` 関数が `/dev/bintime` を利用して実装されました。
* `runtime·read` および `runtime·write` 関数が、内部的に `runtime·pread` および `runtime·pwrite` を呼び出すように再実装されました。
## コアとなるコードの解説
### `src/pkg/runtime/plan9/386/signal.c` の変更
```diff
--- a/src/pkg/runtime/plan9/386/signal.c
+++ b/src/pkg/runtime/plan9/386/signal.c
@@ -4,12 +4,6 @@
#include "runtime.h"
-int64
-runtime·nanotime(void)
-{
- // Won\'t compile.
-}
-
String
runtime·signame(int32)
{
この変更は、以前の runtime·nanotime
のダミー実装(コメントに「Won't compile.」とあるように、実際には機能しないプレースホルダー)を削除したものです。これにより、nanotime
の実際のロジックが src/pkg/runtime/plan9/thread.c
に集約され、コードの整理と正しい実装への移行が行われました。
src/pkg/runtime/plan9/386/sys.s
の変更
--- a/src/pkg/runtime/plan9/386/sys.s
+++ b/src/pkg/runtime/plan9/386/sys.s
@@ -14,16 +14,14 @@ TEXT runtime·open(SB),7,$0
INT $64
RET
-// TODO(ality): remove use of deprecated system calls
--
-TEXT runtime·read(SB),7,$0
- MOVL $15, AX
- INT $64
-+TEXT runtime·pread(SB),7,$0
-+ MOVL $50, AX
-+ INT $64
RET
-TEXT runtime·write(SB),7,$0
- MOVL $20, AX
- INT $64
-+TEXT runtime·pwrite(SB),7,$0
-+ MOVL $51, AX
-+ INT $64
RET
TEXT runtime·close(SB),7,$0
@@ -90,9 +88,9 @@ TEXT runtime·rfork(SB),7,$0
MOVL 0(BX), BX
// more paranoia; check that stack splitting code works
-- PUSHAL
-+ PUSHL SI
CALL\truntime·emptyfunc(SB)\n- POPAL
-+ POPL SI
\t
CALL\tSI\t// fn()\n CALL\truntime·exit(SB)\n```
このアセンブリコードの変更は、Plan 9のシステムコールインターフェースへの適応と、スタック操作の最適化を示しています。
* **`read`/`write` から `pread`/`pwrite` への移行**:
* `TEXT runtime·read(SB)` と `TEXT runtime·write(SB)` の定義が削除されました。これらはそれぞれシステムコール番号 `15` と `20` を使用していました。
* 新しく `TEXT runtime·pread(SB)` と `TEXT runtime·pwrite(SB)` が追加されました。これらはそれぞれシステムコール番号 `50` と `51` を使用します。`MOVL $XX, AX` は、システムコール番号を `AX` レジスタにロードする一般的なパターンです。`INT $64` はPlan 9におけるシステムコール割り込みです。この変更により、GoランタイムはPlan 9の推奨するポジショナルI/Oシステムコールを直接利用できるようになりました。
* **スタック操作の最適化**:
* `runtime·rfork` 関数内で、`PUSHAL` と `POPAL` が `PUSHL SI` と `POPL SI` に置き換えられました。`PUSHAL`/`POPAL` はすべての汎用レジスタをスタックに保存/復元するため、オーバーヘッドが大きいです。一方、`PUSHL SI`/`POPL SI` は `SI` レジスタのみを保存/復元するため、より効率的です。この変更は、スタック分割のテストコードの文脈で行われており、特定のレジスタのみを保存すれば十分な場合に、より軽量なスタック操作を行うことでパフォーマンスを向上させる意図があります。
### `src/pkg/runtime/plan9/os.h` の変更
```diff
--- a/src/pkg/runtime/plan9/os.h
+++ b/src/pkg/runtime/plan9/os.h
@@ -4,6 +4,8 @@
// Plan 9-specific system calls
int32 runtime·open(uint8 *file, int32 mode);\n+int32 runtime·pread(int32 fd, void *buf, int32 nbytes, int64 offset);\n+int32 runtime·pwrite(int32 fd, void *buf, int32 nbytes, int64 offset);\n int32 runtime·read(int32 fd, void *buf, int32 nbytes);\n int32 runtime·close(int32 fd);\n void runtime·exits(int8* msg);\n@@ -16,9 +18,14 @@ int32 runtime·plan9_semrelease(uint32 *addr, int32 count);\n /* open */\n enum\n {\n-\tOREAD = 0,\n-\tOWRITE = 1,\n-\tORDWR = 2\n+\tOREAD = 0,\n+\tOWRITE = 1,\n+\tORDWR = 2,\n+\tOEXEC = 3,\n+\tOTRUNC = 16,\n+\tOCEXEC = 32,\n+\tORCLOSE = 64,\n+\tOEXCL = 0x1000\n };\n \n /* rfork */
このヘッダーファイルの変更は、GoランタイムがPlan 9のシステムコールをより完全にサポートするためのものです。
pread
/pwrite
の宣言:int32 runtime·pread(...)
とint32 runtime·pwrite(...)
の関数プロトタイプが追加されました。これにより、GoのCgo(GoとCの相互運用機能)を通じて、これらのPlan 9システムコールをGoコードから呼び出すためのインターフェースが提供されます。open
フラグの拡張:enum
定義にOEXEC
,OTRUNC
,OCEXEC
,ORCLOSE
,OEXCL
といった新しいファイルオープンフラグが追加されました。これらはPlan 9のopen
システムコールで利用可能な標準的なオプションであり、Goプログラムがファイルを開く際の挙動をより細かく制御できるようになります。
src/pkg/runtime/plan9/thread.c
の変更
--- a/src/pkg/runtime/plan9/thread.c
+++ b/src/pkg/runtime/plan9/thread.c
@@ -69,6 +69,34 @@ runtime·usleep(uint32 µs)\n runtime·sleep(ms);\n }\n \n+int64\n+runtime·nanotime(void)\n+{\n+\tstatic int32 fd = -1;\n+\tbyte b[8];\n+\tuint32 hi, lo;\n+\n+\t// As long as all goroutines share the same file\n+\t// descriptor table we can get away with using\n+\t// just a static fd. Without a lock the file can\n+\t// be opened twice but that\'s okay.\n+\t//\n+\t// Using /dev/bintime gives us a latency on the\n+\t// order of ten microseconds between two calls.\n+\t//\n+\t// The naïve implementation (without the cached\n+\t// file descriptor) is roughly four times slower\n+\t// in 9vx on a 2.16 GHz Intel Core 2 Duo.\n+\t\n+\tif(fd < 0 && (fd = runtime·open((byte*)\"/dev/bintime\", OREAD|OCEXEC)) < 0)\n+\t\treturn 0;\n+\tif(runtime·pread(fd, b, sizeof b, 0) != sizeof b)\n+\t\treturn 0;\n+\thi = b[0]<<24 | b[1]<<16 | b[2]<<8 | b[3];\n+\tlo = b[4]<<24 | b[5]<<16 | b[6]<<8 | b[7];\n+\treturn (int64)hi<<32 | (int64)lo;\n+}\n+\n extern Tos *_tos;\n void\n runtime·exit(int32)\n@@ -183,3 +211,15 @@ void\n runtime·sigpanic(void)\n {\n }\n+\n+int32\n+runtime·read(int32 fd, void *buf, int32 nbytes)\n+{\n+\treturn runtime·pread(fd, buf, nbytes, -1LL);\n+}\n+\n+int32\n+runtime·write(int32 fd, void *buf, int32 nbytes)\n+{\n+\treturn runtime·pwrite(fd, buf, nbytes, -1LL);\n+}\n```
このファイルは、GoランタイムのPlan 9固有のスレッドおよびシステムコール関連のロジックを扱います。
* **`runtime·nanotime` の実装**:
* `runtime·nanotime` 関数は、`static int32 fd = -1;` を使用して `/dev/bintime` のファイルディスクリプタをキャッシュします。これにより、初回呼び出し時に `/dev/bintime` を `OREAD|OCEXEC` モードで開き、以降の呼び出しでは既存のファイルディスクリプタを再利用します。`OCEXEC` フラグは、`exec` システムコール時にこのファイルディスクリプタがクローズされることを保証します。
* `runtime·pread(fd, b, sizeof b, 0)` を呼び出して、`/dev/bintime` から8バイトのデータを読み込みます。オフセット `0` はファイルの先頭から読み込むことを意味します。
* 読み込んだ8バイトのデータ `b` は、`hi` (上位4バイト) と `lo` (下位4バイト) に分割され、ビットシフトと論理和 (`|`) 演算によって64ビットの `int64` 型のナノ秒値に結合されます。この結合方法は、Plan 9の `/dev/bintime` のデータフォーマットに準拠しています。
* **`runtime·read` および `runtime·write` の再実装**:
* `runtime·read` は `runtime·pread(fd, buf, nbytes, -1LL)` を呼び出すように変更されました。
* `runtime·write` は `runtime·pwrite(fd, buf, nbytes, -1LL)` を呼び出すように変更されました。
* `offset` に `-1LL` を渡すことで、これらの関数は従来の `read`/`write` と同様に、ファイルディスクリプタの現在の位置から読み書きを行うように動作します。これは、既存のコードベースとの互換性を保ちつつ、内部的にはより堅牢なポジショナルI/O関数を利用するためのアプローチです。
これらの変更により、GoランタイムはPlan 9環境下で高精度な時間情報を取得できるようになり、また、より現代的で堅牢なファイルI/Oシステムコールを利用するようになりました。
## 関連リンク
* Go言語の公式ドキュメント: [https://golang.org/doc/](https://golang.org/doc/)
* Plan 9 from Bell Labs: [https://9p.io/plan9/](https://9p.io/plan9/)
* Plan 9のシステムコールに関するドキュメント (例: `man 2 read` や `man 2 pread` など): Plan 9のインストール環境やオンラインリソースで参照可能。
## 参考にした情報源リンク
* Go言語のソースコード (特に `src/pkg/runtime` ディレクトリ)
* Plan 9のシステムコールに関する一般的な情報源
* x86アセンブリ言語の命令セットリファレンス
* Goのコードレビューシステム (Gerrit) のCL (Change List) 5327063: [https://golang.org/cl/5327063](https://golang.org/cl/5327063) (コミットメッセージに記載)
* `/dev/bintime` に関するPlan 9のドキュメントや議論 (Web検索で得られた情報)
* `pread` および `pwrite` に関するPOSIX標準またはPlan 9のドキュメント (Web検索で得られた情報)