[インデックス 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) // newselectSYS_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など他のスリープ関数との関連性に関する情報。