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

[インデックス 13923] ファイルの概要

このコミットは、Go言語のランタイムにおいて、Plan 9オペレーティングシステム上の64-bit (amd64) アーキテクチャでのプロセスID (PID) の取得方法を変更するものです。具体的には、これまで使用されていた _tos (Task/Thread Operating System structure) からのオフセットによるPID取得を廃止し、代わりにスレッドローカルストレージ (TLS) からPIDを取得するように変更しています。これにより、_tos オフセットの扱いに伴う煩雑さや問題が解消され、より堅牢なPID取得メカニズムが導入されました。

コミット

commit e42788628aa787cac8d7c61d593f6a17b9c42d08
Author: Akshat Kumar <seed@mail.nanosouffle.net>
Date:   Mon Sep 24 12:24:45 2012 -0400

    cmd/dist, pkg/runtime: Plan 9, 64-bit: Get PID from TLS; remove use of `_tos'.
    
    Using offsets from Tos is cumbersome and we've had problems
    in the past. Since it's only being used to grab the PID, we'll just
    get that from the default TLS instead.
    
    R=rsc, rminnich, npe
    CC=golang-dev
    https://golang.org/cl/6543049

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/e42788628aa787cac8d7c61d593f6a17b9c42d08

元コミット内容

cmd/dist, pkg/runtime: Plan 9, 64-bit: Get PID from TLS; remove use of `_tos'.

Using offsets from Tos is cumbersome and we've had problems
in the past. Since it's only being used to grab the PID, we'll just
get that from the default TLS instead.

R=rsc, rminnich, npe
CC=golang-dev
https://golang.org/cl/6543049

変更の背景

この変更の主な背景は、GoランタイムがPlan 9オペレーティングシステム上でプロセスID (PID) を取得する際に、_tos (Task/Thread Operating System structure) からのオフセットを利用する方法が「煩雑 (cumbersome)」であり、過去に「問題 (problems)」を引き起こしていたためです。

_tos はPlan 9におけるスレッドごとのデータ構造であり、これを通じてPIDなどの情報にアクセスすることが可能でした。しかし、オフセットを用いて特定のデータにアクセスする方法は、構造体のレイアウト変更やコンパイラの最適化などによって、予期せぬ問題やメンテナンスの困難さを引き起こす可能性があります。特に、PIDの取得という単一の目的のためにこの複雑なメカニズムを使用することは、過剰なオーバーヘッドとリスクを伴いました。

そこで、よりシンプルで堅牢な方法として、スレッドローカルストレージ (TLS) を利用してPIDを取得する方針が採用されました。TLSは、各スレッドが独自のデータを持つための標準的なメカニズムであり、OSやコンパイラによって適切に管理されるため、オフセットの管理に伴う問題を回避できます。この変更は、Goランタイムの安定性と保守性を向上させることを目的としています。

前提知識の解説

このコミットを理解するためには、以下の概念について理解しておく必要があります。

  • Plan 9 from Bell Labs: ベル研究所が開発した分散オペレーティングシステムです。Unixの概念をさらに推し進め、すべてをファイルとして扱うという思想が特徴です。Go言語の開発者の一部はPlan 9の開発にも携わっており、GoランタイムにはPlan 9向けのサポートが含まれています。
  • amd64 (x86-64): 64ビットの命令セットアーキテクチャで、IntelとAMDによって開発されました。現代のほとんどのデスクトップおよびサーバーCPUで採用されています。
  • プロセスID (PID): オペレーティングシステムが実行中のプロセスを一意に識別するために割り当てる番号です。
  • スレッドローカルストレージ (TLS: Thread-Local Storage): 各スレッドがそれぞれ独立したデータを保持するためのメカニズムです。グローバル変数とは異なり、TLSに格納されたデータは同じプロセス内の他のスレッドからは直接アクセスできません。これにより、スレッド間のデータ競合を防ぎ、並行処理のプログラミングを容易にします。x86-64アーキテクチャでは、GS レジスタがTLSのベースアドレスを指すために使用されることがあります。
  • Goランタイム (Go Runtime): Goプログラムの実行を管理する部分です。ガベージコレクション、スケジューリング、システムコールインターフェースなど、Go言語の並行処理モデルとメモリ管理を支える低レベルの機能を提供します。ランタイムの多くはアセンブリ言語で書かれており、特定のアーキテクチャやOSに依存する部分が多く存在します。
  • _tos (Task/Thread Operating System structure): Plan 9において、各スレッド(またはタスク)に関する情報が格納されているデータ構造です。これには、スレッドのスタックポインタ、レジスタの状態、そしてPIDなどの情報が含まれることがあります。この構造体へのアクセスは通常、特定のレジスタ(例えば R15FS / GS セグメントレジスタ)を介して行われますが、そのオフセットはOSのバージョンやコンパイラの設定によって変動する可能性があり、直接オフセットに依存するコードは脆いとされます。
  • アセンブリ言語 (.s ファイル): コンピュータのCPUが直接実行できる機械語命令を、人間が読める形式で記述した低レベルのプログラミング言語です。Goランタイムのパフォーマンスが重要な部分や、OSとの直接的なインターフェース部分はアセンブリ言語で記述されることが多いです。
  • src/cmd/dist/buildruntime.c: Goのビルドシステムの一部で、ランタイムのアセンブリコードを生成する際に使用されるC言語のファイルです。アセンブリコード内で使用されるマクロなどを定義することがあります。
  • src/pkg/runtime/defs_plan9_amd64.h: Plan 9 amd64アーキテクチャ向けのランタイム定義を含むヘッダーファイルです。定数や構造体のオフセットなどが定義されます。
  • src/pkg/runtime/rt0_plan9_amd64.s: Plan 9 amd64アーキテクチャ向けのランタイムのエントリポイント(プログラムの開始点)となるアセンブリファイルです。
  • src/pkg/runtime/sys_plan9_amd64.s: Plan 9 amd64アーキテクチャ向けのシステムコールや低レベルのランタイム関数を含むアセンブリファイルです。

技術的詳細

このコミットは、Plan 9上のGoランタイム (amd64) におけるPID取得の根本的な変更を伴います。

  1. _tos 依存の排除: 以前は、_tos グローバルシンボル(またはそれに相当するメモリ領域)に格納された構造体から、特定のオフセット(例: tos_pid)を用いてPIDを取得していました。この方法は、_tos 構造体のレイアウト変更に脆弱であり、メンテナンス上の問題を引き起こしていました。
  2. TLSへの移行: 新しいアプローチでは、PIDをスレッドローカルストレージ (TLS) に格納し、GS セグメントレジスタを介してアクセスするように変更されました。GS レジスタはx86-64アーキテクチャにおいて、TLSのベースアドレスを指すために慣習的に使用されます。これにより、PIDへのアクセスがより標準的で堅牢な方法に統一されます。
  3. マクロの導入: src/cmd/dist/buildruntime.c に、Plan 9 amd64向けにTLSからg (goroutine構造体)、m (machine構造体)、そしてprocid (PID) を取得するための新しいアセンブリマクロが定義されました。これにより、アセンブリコード内でのTLSアクセスが抽象化され、コードの可読性と保守性が向上します。
    • get_tls(r): TLSのベースアドレスを取得するマクロ(このコミットでは空だが、将来的な拡張性のためか、あるいは特定のレジスタがTLSベースを指すことを前提としているためか)。
    • g(r): GS レジスタからのオフセット0にgoroutine構造体へのポインタがあることを示すマクロ。
    • m(r): GS レジスタからのオフセット8にmachine構造体へのポインタがあることを示すマクロ。
    • procid(r): GS レジスタからのオフセット16にPIDがあることを示すマクロ。
  4. アセンブリコードの修正:
    • src/pkg/runtime/rt0_plan9_amd64.s から、_tos グローバルシンボルの初期化と宣言が削除されました。これは、_tos がもはやPID取得に必要ないためです。
    • src/pkg/runtime/sys_plan9_amd64.s では、runtime·rfork 関数内でPIDを取得する部分が変更されました。以前は _tos(SB) から tos_pid(AX) を介してPIDを取得していましたが、新しいコードでは procid(AX) マクロ(実体は 16(GS))を使用してTLSから直接PIDを取得するように変更されています。

この変更により、GoランタイムはPlan 9環境でのPID取得において、より現代的で安定したアプローチを採用することになります。

コアとなるコードの変更箇所

src/cmd/dist/buildruntime.c の変更

--- a/src/cmd/dist/buildruntime.c
+++ b/src/cmd/dist/buildruntime.c
@@ -145,6 +145,12 @@ static struct {
 		"#define\tg(r) 0(r)\\n"\n
 		"#define\tm(r) 8(r)\\n"\n
 	},\n
+\t{\"amd64\", \"plan9\",\n
+\t\t"#define\tget_tls(r)\\n"\n
+\t\t"#define\tg(r) 0(GS)\\n"\n
+\t\t"#define\tm(r) 8(GS)\\n"\n
+\t\t"#define\tprocid(r) 16(GS)\\n"\n
+\t},\
 	{\"amd64\", \"\",\n
 		"// The offsets 0 and 8 are known to:\\n"\n
 		"//\t../../cmd/6l/pass.c:/D_GS\\n"\

src/pkg/runtime/defs_plan9_amd64.h の変更

--- a/src/pkg/runtime/defs_plan9_amd64.h
+++ b/src/pkg/runtime/defs_plan9_amd64.h
@@ -1,3 +1,2 @@
 // nothing to see here
-#define tos_pid 64
 #define PAGESIZE 0x200000ULL

src/pkg/runtime/rt0_plan9_amd64.s の変更

--- a/src/pkg/runtime/rt0_plan9_amd64.s
+++ b/src/pkg/runtime/rt0_plan9_amd64.s
@@ -3,11 +3,9 @@
 // license that can be found in the LICENSE file.\n
  
 TEXT _rt0_amd64_plan9(SB),7, $0
-\tMOVQ\tAX, _tos(SB)
 \tMOVQ\t$_rt0_amd64(SB), AX
 \tMOVQ\tSP, DI
 \tJMP\tAX
 \n
  DATA runtime·isplan9(SB)/4, $1
  GLOBL runtime·isplan9(SB), $4
-GLOBL _tos(SB), $8

src/pkg/runtime/sys_plan9_amd64.s の変更

--- a/src/pkg/runtime/sys_plan9_amd64.s
+++ b/src/pkg/runtime/sys_plan9_amd64.s
@@ -105,9 +105,8 @@ TEXT runtime·rfork(SB),7,$0
 	MOVQ	DX, g(AX)
 	MOVQ	BX, m(AX)
 \n
-\t// Initialize AX from _tos->pid
-\tMOVQ\t_tos(SB), AX
-\tMOVQ\ttos_pid(AX), AX
+\t// Initialize AX from pid in TLS.
+\tMOVQ\tprocid(AX), AX
 	MOVQ	AX, m_procid(BX)	// save pid as m->procid
 	
 	CALL	runtime·stackcheck(SB)	// smashes AX, CX

コアとなるコードの解説

src/cmd/dist/buildruntime.c

このファイルは、Goのビルドプロセスにおいて、ランタイムのアセンブリコードを生成する際に使用されるマクロを定義しています。追加されたブロックは、amd64 アーキテクチャと plan9 オペレーティングシステムの組み合わせに特化したものです。

  • #define get_tls(r)\n": get_tls マクロを定義していますが、このコミットでは空です。これは、GS レジスタが既にTLSのベースアドレスを保持していることを前提としているか、あるいは将来的な拡張のためにプレースホルダーとして残されている可能性があります。
  • #define g(r) 0(GS)\\n": g マクロを定義しています。これは、GS レジスタが指すアドレスからオフセット0の位置に、現在のgoroutine (g) 構造体へのポインタがあることを示します。
  • #define m(r) 8(GS)\\n": m マクロを定義しています。これは、GS レジスタが指すアドレスからオフセット8の位置に、現在のmachine (m) 構造体へのポインタがあることを示します。
  • #define procid(r) 16(GS)\\n": procid マクロを定義しています。これは、GS レジスタが指すアドレスからオフセット16の位置に、プロセスID (PID) が格納されていることを示します。これらのマクロは、アセンブリコード内でTLSからのデータアクセスを簡潔に記述するために使用されます。

src/pkg/runtime/defs_plan9_amd64.h

このヘッダーファイルは、Plan 9 amd64アーキテクチャ向けのランタイム定義を含んでいます。

  • #define tos_pid 64 の行が削除されました。これは、_tos 構造体からPIDを取得するためのオフセット定義が不要になったことを意味します。

src/pkg/runtime/rt0_plan9_amd64.s

このファイルは、Plan 9 amd64アーキテクチャ向けのGoランタイムのエントリポイントです。

  • MOVQ AX, _tos(SB) の行が削除されました。これは、プログラム開始時に _tos グローバルシンボルを初期化する処理が不要になったためです。
  • GLOBL _tos(SB), $8 の行が削除されました。これは、_tos グローバルシンボルの宣言が不要になったためです。

src/pkg/runtime/sys_plan9_amd64.s

このファイルは、Plan 9 amd64アーキテクチャ向けのシステムコールや低レベルのランタイム関数を含んでいます。特に runtime·rfork 関数は、Plan 9における rfork システムコール(プロセスや名前空間の複製を行う)のラッパーです。

  • 以前のコードでは、PIDを取得するために以下の2行が使用されていました。
    • MOVQ _tos(SB), AX: _tos グローバルシンボルのアドレスを AX レジスタにロードします。
    • MOVQ tos_pid(AX), AX: AX レジスタが指すアドレス(_tos のベースアドレス)から tos_pid オフセット(以前は64)の位置にある値を AX レジスタにロードします。これがPIDでした。
  • 新しいコードでは、これらの2行が以下の1行に置き換えられました。
    • MOVQ procid(AX), AX: ここでの procid(AX) は、src/cmd/dist/buildruntime.c で定義されたマクロ 16(GS) に展開されます。つまり、GS レジスタが指すTLSのベースアドレスからオフセット16の位置にある値を AX レジスタにロードします。これにより、TLSから直接PIDが取得されるようになります。

これらの変更により、GoランタイムはPlan 9環境でのPID取得において、_tos 構造体のオフセットに依存する脆い方法から、TLSを利用するより堅牢で標準的な方法へと移行しました。

関連リンク

参考にした情報源リンク