[インデックス 11103] ファイルの概要
このコミットは、Go言語のランタイムにおけるruntime.usleep()
関数のバグ修正に関するものです。具体的には、darwin/amd64
およびlinux/arm
アーキテクチャにおけるシステムコール番号の誤りや、linux/arm
におけるusleep
の実装における除算・剰余演算の誤りを修正しています。これにより、これらの環境でのruntime.usleep()
の正確な動作が保証されます。
コミット
commit 1250f94f93402a555a7dc905dfc5b0acc85c0b98
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Tue Jan 10 20:48:02 2012 -0800
runtime: runtime.usleep() bugfix on darwin/amd64 and linux/arm
pkg/runtime/sys_darwin_amd64.s: fixes syscall select nr
pkg/runtime/sys_linux_arm.s: uses newselect instead of the now unimplemented
(old) select, also fixes the wrong div/mod statements in runtime.usleep.
Fixes #2633
R=golang-dev, dave, rsc
CC=golang-dev
https://golang.org/cl/5504096
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/1250f94f93402a555a7dc905dfc5b0acc85c0b98
元コミット内容
Goランタイムのruntime.usleep()
関数におけるバグ修正。darwin/amd64
ではselect
システムコールの番号を修正し、linux/arm
では廃止された古いselect
システムコールの代わりにnewselect
を使用するように変更し、さらにruntime.usleep
内の誤った除算/剰余演算の記述を修正しました。
変更の背景
このコミットは、Goランタイムの内部関数であるruntime.usleep()
が、特定のアーキテクチャ(darwin/amd64
とlinux/arm
)で正しく動作しないバグを修正するために行われました。コミットメッセージに「Fixes #2633」とあることから、Goプロジェクトの当時のイシュートラッカーで報告された問題(イシュー2633)に対応するものです。このバグは、システムコール番号の誤りや、時間計算におけるロジックの誤りに起因していました。特に、linux/arm
環境では、古いselect
システムコールが使用されており、これが原因で問題が発生していました。
前提知識の解説
1. runtime.usleep()
runtime.usleep()
は、Go言語のランタイムが内部的に使用する関数で、指定されたマイクロ秒数だけ現在のゴルーチン(またはスレッド)の実行を一時停止させるために使われます。これは、GoプログラムがI/O操作などで待機する際に、CPUリソースを効率的に利用するために重要です。この関数はGoのユーザーコードから直接呼び出すことは通常なく、ランタイムのスケジューラやガベージコレクタなどの低レベルな部分で利用されます。
2. システムコール (System Calls)
システムコールは、ユーザー空間で動作するプログラムが、カーネル空間で提供されるサービス(ファイルI/O、メモリ管理、プロセス管理、ネットワーク通信など)を利用するためのインターフェースです。プログラムがハードウェアに直接アクセスしたり、他のプロセスと通信したりする場合、システムコールを介してカーネルに処理を依頼します。各システムコールには一意の番号(システムコール番号)が割り当てられており、プログラムはこれらの番号を使って目的のシステムコールを呼び出します。アーキテクチャやOSのバージョンによって、システムコール番号は異なる場合があります。
3. select
とnewselect
システムコール
select
は、Unix系OSでI/O多重化(複数のファイルディスクリプタからのI/Oイベントを監視する)を行うためのシステムコールです。しかし、Linuxカーネルの進化に伴い、より新しいnewselect
というシステムコールが導入されました。これは、古いselect
が抱えていたファイルディスクリプタ数の上限(通常1024)などの制限を緩和するために設計されました。古いシステムではselect
が使われていましたが、新しいシステムではnewselect
が推奨され、古いselect
は廃止されるか、newselect
へのラッパーとして実装されることがあります。このコミットでは、linux/arm
で廃止された古いselect
の代わりにnewselect
を使用するように変更されています。
4. アセンブリ言語 (.s
ファイル)
Go言語のランタイムは、パフォーマンスが非常に重要となる低レベルな処理(ゴルーチンのスケジューリング、スタック管理、システムコール呼び出しなど)において、C言語やGo言語だけでなく、アセンブリ言語で記述されたコードも使用します。.s
ファイルはアセンブリ言語のソースコードファイルを示し、特定のCPUアーキテクチャ(例: amd64
, arm
)に特化した命令セットを用いて、非常に効率的な処理を実現します。システムコールの呼び出しは、通常、アセンブリ言語で直接記述されることが多いです。
5. darwin/amd64
とlinux/arm
darwin/amd64
: macOSオペレーティングシステム上で動作するIntel 64ビット(x86-64)アーキテクチャのシステムを指します。linux/arm
: Linuxオペレーティングシステム上で動作するARMアーキテクチャのシステムを指します。ARMは、モバイルデバイスや組み込みシステムで広く使用されているプロセッサアーキテクチャです。
これらのアーキテクチャは、それぞれ異なるシステムコール規約やアセンブリ命令セットを持っています。
技術的詳細
このコミットは、主に2つのファイル、src/pkg/runtime/sys_darwin_amd64.s
とsrc/pkg/runtime/sys_linux_arm.s
に対する変更を含んでいます。
src/pkg/runtime/sys_darwin_amd64.s
の変更
このファイルでは、runtime.usleep
関数内でselect
システムコールを呼び出す際のシステムコール番号が修正されています。
変更前: MOVL $(0x2000000+23), AX
変更後: MOVL $(0x2000000+93), AX
0x2000000
は、macOS(Darwin)におけるシステムコール番号のベースアドレスです。これに続く数値が実際のシステムコール番号を示します。
- 変更前は
23
が使用されていましたが、これは誤ったselect
システムコール番号でした。 - 変更後は
93
に修正されています。これは、darwin/amd64
における正しいselect
システムコール番号です。システムコール番号の誤りは、usleep
が正しく機能しない原因となります。
src/pkg/runtime/sys_linux_arm.s
の変更
このファイルでは、2つの重要な修正が行われています。
-
select
システムコール番号の修正とnewselect
への切り替え: 変更前:#define SYS_select (SYS_BASE + 82)
変更後:#define SYS_select (SYS_BASE + 142) // newselect
SYS_BASE
はLinuxにおけるシステムコール番号のベースアドレスです。- 変更前は
82
がselect
システムコールとして定義されていました。これは古いselect
システムコールの番号です。 - 変更後は
142
に修正され、コメントでnewselect
と明記されています。これは、Linux ARM環境で推奨されるnewselect
システムコールの番号です。古いselect
が廃止されたり、正しく動作しなくなった環境に対応するための変更です。
- 変更前は
-
runtime.usleep
内の除算・剰余演算の修正:runtime.usleep
関数内で、マイクロ秒を秒とナノ秒に変換するための除算と剰余演算のロジックが修正されています。変更前:
DIV R1, R0 MOD R2, R0 MOVW R1, 4(SP) MOVW R2, 8(SP)
変更後:
DIV R2, R0 MOD R2, R1 MOVW R0, 4(SP) MOVW R1, 8(SP)
このアセンブリコードは、
usleep
に渡されたマイクロ秒(usec
)を秒と残りのマイクロ秒に分解するためのものです。R0
には入力のマイクロ秒(usec
)が、R2
には100万(1,000,000、マイクロ秒から秒への変換係数)が格納されています。- 変更前は
DIV R1, R0
となっており、R1
が何であるか不明確で、除算のオペランドが誤っていました。また、剰余演算の結果もR0
に格納されていました。 - 変更後では、
DIV R2, R0
でR0
をR2
(100万)で除算し、商(秒数)をR0
に格納します。 MOD R2, R1
でR1
(元のマイクロ秒)をR2
(100万)で除算し、剰余(残りのマイクロ秒)をR1
に格納します。- その後、
MOVW R0, 4(SP)
で秒数をスタックに、MOVW R1, 8(SP)
で残りのマイクロ秒をスタックに格納しています。 この修正により、usleep
が正確な時間だけスリープするようになります。
コアとなるコードの変更箇所
src/pkg/runtime/sys_darwin_amd64.s
--- a/src/pkg/runtime/sys_darwin_amd64.s
+++ b/src/pkg/runtime/sys_darwin_amd64.s
@@ -175,7 +175,7 @@ TEXT runtime·usleep(SB),7,$16
MOVL $0, DX
MOVL $0, R10
MOVQ SP, R8
- MOVL $(0x2000000+23), AX
+ MOVL $(0x2000000+93), AX
SYSCALL
RET
src/pkg/runtime/sys_linux_arm.s
--- a/src/pkg/runtime/sys_linux_arm.s
+++ b/src/pkg/runtime/sys_linux_arm.s
@@ -34,7 +34,7 @@
#define SYS_gettid (SYS_BASE + 224)
#define SYS_tkill (SYS_BASE + 238)
#define SYS_sched_yield (SYS_BASE + 158)
-#define SYS_select (SYS_BASE + 82)
+#define SYS_select (SYS_BASE + 142) // newselect
#define ARM_BASE (SYS_BASE + 0x0f0000)
#define SYS_ARM_cacheflush (ARM_BASE + 2)
@@ -313,10 +313,10 @@ TEXT runtime·usleep(SB),7,$12
MOVW usec+0(FP), R0
MOVW R0, R1
MOVW $1000000, R2
-\tDIV\tR1, R0
-\tMOD\tR2, R0
-\tMOVW\tR1, 4(SP)\n-\tMOVW\tR2, 8(SP)
+\tDIV\tR2, R0
+\tMOD\tR2, R1
+\tMOVW\tR0, 4(SP)
+\tMOVW\tR1, 8(SP)
MOVW $0, R0
MOVW $0, R1
MOVW $0, R2
コアとなるコードの解説
src/pkg/runtime/sys_darwin_amd64.s
の変更点
MOVL $(0x2000000+23), AX
からMOVL $(0x2000000+93), AX
への変更: これは、AX
レジスタにロードされるシステムコール番号を修正しています。0x2000000
はDarwin(macOS)におけるシステムコール番号のオフセットであり、それに続く数値が実際のシステムコール番号です。以前は23
という誤った番号がselect
システムコールとして使用されていましたが、正しいselect
システムコール番号である93
に修正されました。これにより、runtime.usleep
がselect
システムコールを介して正しくスリープできるようになります。
src/pkg/runtime/sys_linux_arm.s
の変更点
-
#define SYS_select (SYS_BASE + 82)
から#define SYS_select (SYS_BASE + 142) // newselect
への変更: これは、Linux ARM環境におけるselect
システムコールの定義を更新しています。以前は古いselect
システムコール番号である82
が使用されていましたが、新しいnewselect
システムコール番号である142
に修正されました。これにより、runtime.usleep
が最新のLinuxカーネルで推奨されるnewselect
システムコールを利用して、より堅牢かつ正確に動作するようになります。 -
runtime·usleep
関数内の除算・剰余演算の修正:DIV R1, R0
からDIV R2, R0
へ: これは除算命令のオペランドを修正しています。R0
にはスリープすべきマイクロ秒数(usec
)が、R2
には100万(マイクロ秒から秒への変換係数)が格納されています。変更前はR1
で除算しようとしていましたが、これは誤りでした。変更後はR2
で除算することで、R0
に秒数(商)が正しく計算されます。MOD R2, R0
からMOD R2, R1
へ: これは剰余命令のオペランドと結果の格納先を修正しています。変更前はR0
に剰余を格納しようとしていましたが、これは秒数と競合する可能性がありました。変更後はR1
に剰余(残りのマイクロ秒)を格納することで、秒数と残りのマイクロ秒がそれぞれ異なるレジスタに正しく保持されます。MOVW R1, 4(SP)
とMOVW R2, 8(SP)
からMOVW R0, 4(SP)
とMOVW R1, 8(SP)
へ: これは、計算された秒数と残りのマイクロ秒をスタックに格納する際のレジスタを修正しています。変更後は、R0
に格納された秒数をスタックオフセット4(SP)
に、R1
に格納された残りのマイクロ秒をスタックオフセット8(SP)
に格納することで、usleep
がシステムコールに渡す引数が正しく設定されるようになります。
これらの変更により、runtime.usleep()
はdarwin/amd64
とlinux/arm
の両方で、システムコールを正しく呼び出し、かつ正確な時間計算に基づいてスリープを実行できるようになりました。
関連リンク
参考にした情報源リンク
- Go言語のランタイム、システムコール、アセンブリに関する一般的な知識
- Linuxシステムコール(
select
,newselect
)に関するドキュメント - ARMアセンブリの基本的な命令セットとレジスタの使用法
- Darwin(macOS)システムコールに関する情報
- Go言語のイシュートラッカー(#2633の具体的な内容は、古いイシューのため直接参照できませんでしたが、コミットメッセージからその目的を推測しました。)
usleep
関数の一般的な動作と、nanosleep
など他のスリープ関数との関連性に関する情報。