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

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

このコミットは、GoランタイムがNetBSD上でLWP (Lightweight Process) のprocidを適切に初期化するように変更するものです。特に、Cgoを使用する際にLWPのアンパークが正しく機能するために、procidが常にminit()関数内で初期化されるように修正されています。

コミット

commit deb93b0f7b646faabc7b4c5db49d7a586a17247e
Author: Joel Sing <jsing@google.com>
Date:   Thu May 31 03:27:04 2012 +1000

    runtime: always initialise procid on netbsd
    
    The correct procid is needed for unparking LWPs on NetBSD - always
    initialise procid in minit() so that cgo works correctly. The non-cgo
    case already works correctly since procid is initialised via
    lwp_create().
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/6257071

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

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

元コミット内容

このコミットは、NetBSD環境におけるGoランタイムの動作を改善することを目的としています。具体的には、LWP (Lightweight Process) のprocid(プロセスIDに相当するLWP識別子)の初期化方法を変更し、特にCgo(GoとC言語の相互運用機能)を使用する際のLWPのアンパーク(休止状態のLWPを再開させる操作)が正しく行われるようにします。

変更前は、非Cgoの場合にはlwp_create()を通じてprocidが初期化されていましたが、Cgoを使用する場合にはprocidが正しく設定されない可能性がありました。このコミットでは、minit()関数内で常にruntime·lwp_self()システムコールを呼び出して現在のLWPのprocidを取得し、それをm->procidに設定することで、この問題を解決しています。これにより、Cgoの有無にかかわらず、LWPのアンパークに必要な正しいprocidが常に利用可能になります。

変更の背景

Goランタイムは、OSのスレッド(NetBSDではLWP)を抽象化してゴルーチンをスケジューリングします。LWPは、GoのM(Machine)構造体に対応し、実際のOSスレッドを表します。Mは、実行中のゴルーチン(G)を管理し、必要に応じてLWPをパーク(休止)またはアンパーク(再開)します。

NetBSDでは、LWPをアンパークする際に、対象となるLWPの正確な識別子(procid)が必要です。コミットメッセージによると、Cgoを使用しない通常のGoプログラムでは、LWPが作成される際にprocidが適切に初期化されていました。しかし、Cgoが絡むシナリオでは、このprocidが正しく設定されないケースがあり、その結果、LWPのアンパークが失敗し、プログラムのデッドロックや異常終了につながる可能性がありました。

この問題は、GoランタイムがCgoと連携して動作する際の安定性と信頼性に影響を与えるため、procidの初期化をより堅牢な方法で行う必要がありました。具体的には、Goランタイムの初期化フェーズであるminit()において、常に現在のLWPのprocidを取得し、m構造体に格納することで、Cgoの有無にかかわらずLWPのアンパークが確実に機能するようにすることが変更の背景です。

前提知識の解説

NetBSDとLWP (Lightweight Process)

  • NetBSD: オープンソースのUNIX系オペレーティングシステムの一つで、高い移植性を特徴としています。様々なハードウェアアーキテクチャで動作します。
  • LWP (Lightweight Process): NetBSDを含む多くのUNIX系OSで採用されているスレッド実装の概念です。LWPはカーネルが管理する実行単位であり、ユーザーレベルのスレッド(Goのゴルーチンなど)がカーネルに処理を依頼する際の基盤となります。一つのプロセスは複数のLWPを持つことができ、各LWPは独立した実行コンテキスト(レジスタ、スタックなど)を持ちます。Goランタイムは、これらのLWPをGoのM(Machine)として利用し、ゴルーチンをスケジューリングします。
  • procid: NetBSDにおけるLWPの識別子です。LWPを特定し、操作(例えば、アンパーク)するために使用されます。これは、一般的なOSにおけるスレッドIDやプロセスIDに似た概念です。

GoランタイムのMとG

  • M (Machine): GoランタイムにおけるOSスレッドの抽象化です。各Mは一つのLWP(またはOSスレッド)に対応し、そのLWP上でゴルーチンを実行します。Mは、スケジューラ、スタック、レジスタなどのコンテキストを持ちます。
  • G (Goroutine): Go言語の軽量な並行処理単位です。Goランタイムによって管理され、M上で実行されます。GはOSスレッドよりもはるかに軽量で、数百万個のGを同時に作成することも可能です。
  • スケジューラ: Goランタイムの重要なコンポーネントで、GをMに割り当てて実行を管理します。Gがブロックされたり、I/O待ちになったりすると、スケジューラは別のGを同じM上で実行させ、LWPの効率的な利用を促進します。

Cgo

  • Cgo: Go言語の機能の一つで、GoプログラムからC言語のコードを呼び出したり、C言語のコードからGoの関数を呼び出したりすることを可能にします。Cgoを使用すると、既存のCライブラリをGoプロジェクトに統合したり、Goでは実装が難しい低レベルの操作を行ったりすることができます。Cgoは、GoランタイムとCランタイムの間でコンテキストの切り替えを伴うため、スレッド管理やシグナル処理において特別な考慮が必要になることがあります。

minit()osinit()

  • runtime·osinit(): GoランタイムがOS固有の初期化を行う関数です。この関数は、プログラムの起動時に一度だけ呼び出され、CPU数やその他のOS関連の設定を初期化します。
  • runtime·minit(): 各M(OSスレッド)が起動する際に呼び出される初期化関数です。この関数は、M固有のリソース(シグナルハンドラ、スタックなど)をセットアップします。

システムコール (sys__lwp_self)

  • システムコール: アプリケーションがOSカーネルのサービスを要求するためのインターフェースです。例えば、ファイル操作、メモリ管理、プロセス管理など、OSの機能を利用する際にシステムコールが使用されます。
  • sys__lwp_self: NetBSDのシステムコールの一つで、現在のLWPの識別子(procid)を返します。このシステムコールは、アセンブリ言語で直接呼び出されます。

技術的詳細

このコミットの技術的な核心は、NetBSDにおけるLWPのprocidの取得と、GoランタイムのM構造体へのその値の格納です。

  1. runtime·lwp_self アセンブリ関数の追加:

    • src/pkg/runtime/sys_netbsd_386.s (32ビットx86アーキテクチャ用) と src/pkg/runtime/sys_netbsd_amd64.s (64ビットx86-64アーキテクチャ用) に、runtime·lwp_selfという新しいアセンブリ関数が追加されました。
    • この関数は、NetBSDのシステムコールであるsys__lwp_selfを呼び出します。
    • sys__lwp_selfのシステムコール番号は311です。
    • 32ビット版 (sys_netbsd_386.s) では、システムコール番号をAXレジスタにロードし、INT $0x80命令でシステムコールを呼び出します。
    • 64ビット版 (sys_netbsd_amd64.s) では、システムコール番号をAXレジスタにロードし、SYSCALL命令でシステムコールを呼び出します。
    • これらのアセンブリ関数は、現在のLWPのprocidを戻り値として返します。
  2. runtime·thread_netbsd.c の変更:

    • runtime·lwp_self関数のプロトタイプ宣言が追加されました: extern int32 runtime·lwp_self(void);
    • runtime·osinit() からの m->procid = 1; の削除:
      • 変更前は、runtime·osinit()関数内でメインスレッドのprocidがハードコードで1に設定されていました。これは、メインスレッドのLWP IDが常に1であるという仮定に基づいています。しかし、この仮定は常に正しいとは限らず、特にCgoが絡む場合に問題を引き起こす可能性がありました。また、osinitはOS全体の初期化であり、個々のMのprocidを設定する場所としては適切ではありません。
    • runtime·minit() への m->procid = runtime·lwp_self(); の追加:
      • runtime·minit()関数は、各M(Goランタイムが管理するOSスレッド)が初期化される際に呼び出されます。
      • この変更により、各Mが起動するたびにruntime·lwp_self()を呼び出し、そのMが対応するLWPの実際のprocidを取得してm->procidに格納するようになりました。
      • これにより、各Mは自身のLWPの正確な識別子を常に持つことになり、LWPのアンパークなどの操作が確実に行えるようになります。特にCgoが関与する場合でも、正しいprocidが保証されるため、LWPのライフサイクル管理が安定します。

この変更により、GoランタイムはNetBSD上でLWPのprocidを動的に取得し、各Mに割り当てることで、Cgoの有無にかかわらずLWPのアンパーク処理の信頼性を向上させています。

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

src/pkg/runtime/sys_netbsd_386.s および src/pkg/runtime/sys_netbsd_amd64.s

--- a/src/pkg/runtime/sys_netbsd_386.s
+++ b/src/pkg/runtime/sys_netbsd_386.s
@@ -292,6 +292,11 @@ TEXT runtime·lwp_unpark(SB),7,$-4
 	INT	$0x80
 	RET
 
+TEXT runtime·lwp_self(SB),7,$-4
+	MOVL	$311, AX		// sys__lwp_self
+	INT	$0x80
+	RET
+
 TEXT runtime·sysctl(SB),7,$28
 	LEAL	arg0+0(FP), SI
 	LEAL	4(SP), DI
--- a/src/pkg/runtime/sys_netbsd_amd64.s
+++ b/src/pkg/runtime/sys_netbsd_amd64.s
@@ -61,6 +61,11 @@ TEXT runtime·lwp_unpark(SB),7,$0
 	SYSCALL
 	RET
 
+TEXT runtime·lwp_self(SB),7,$0
+	MOVL	$311, AX		// sys__lwp_self
+	SYSCALL
+	RET
+
 // Exit the entire program (like C exit)
 TEXT runtime·exit(SB),7,$-8
 	MOVL	8(SP), DI		// arg 1 - exit status

src/pkg/runtime/thread_netbsd.c

--- a/src/pkg/runtime/thread_netbsd.c
+++ b/src/pkg/runtime/thread_netbsd.c
@@ -28,6 +28,7 @@ extern int32 runtime·lwp_create(UcontextT *context, uintptr flags, void *lwpid)\n extern void runtime·lwp_mcontext_init(void *mc, void *stack, M *m, G *g, void (*fn)(void));\n extern int32 runtime·lwp_park(Timespec *abstime, int32 unpark, void *hint, void *unparkhint);\n extern int32 runtime·lwp_unpark(int32 lwp, void *hint);\n+extern int32 runtime·lwp_self(void);\n \n // From NetBSD\'s <sys/sysctl.h>\n #define\tCTL_HW\t6\n@@ -181,9 +182,6 @@ void\n runtime·osinit(void)\n {\n 	runtime·ncpu = getncpu();\n-\n-\t// Main thread is always LWP 1.\n-\tm->procid = 1;\n }\n \n void\n@@ -196,6 +194,8 @@ void\n runtime·minit(void)\n {\n+\tm->procid = runtime·lwp_self();\n+\n \t// Initialize signal handling\n \tm->gsignal = runtime·malg(32*1024);\n \truntime·signalstack((byte*)m->gsignal->stackguard - StackGuard, 32*1024);\n```

## コアとなるコードの解説

### `runtime·lwp_self` アセンブリ関数

このアセンブリ関数は、現在のLWPの`procid`を取得するためのラッパーです。
*   `MOVL $311, AX`: `AX`レジスタにシステムコール番号`311`をロードします。`311`はNetBSDの`sys__lwp_self`システムコールに対応します。
*   `INT $0x80` (32ビット) または `SYSCALL` (64ビット): システムコールを実行します。これにより、カーネルが`sys__lwp_self`を呼び出し、現在のLWPの`procid`を返します。この戻り値は通常、`AX`レジスタに格納されます。
*   `RET`: 関数から戻ります。`AX`レジスタの値が関数の戻り値となります。

この関数は、GoのランタイムコードからCgoを介さずに直接OSのLWP IDを取得するための低レベルなインターフェースを提供します。

### `src/pkg/runtime/thread_netbsd.c` の変更

1.  **`extern int32 runtime·lwp_self(void);`**:
    *   これは、上記で定義されたアセンブリ関数`runtime·lwp_self`をCコードから呼び出すための前方宣言です。これにより、Cコードがアセンブリで実装された関数を利用できるようになります。

2.  **`runtime·osinit()` からの `m->procid = 1;` の削除**:
    *   以前は、GoランタイムのOS初期化フェーズで、メインスレッドの`procid`が静的に`1`に設定されていました。これは、多くのUNIX系システムでメインスレッドのIDが`1`であることが多いためですが、NetBSDのLWP IDの割り当てが常にこの仮定に従うとは限りません。特に、Cgoが介入するような複雑なシナリオでは、この静的な設定が問題を引き起こす可能性がありました。この行を削除することで、`procid`の初期化をより動的かつ正確な方法に委ねます。

3.  **`runtime·minit()` への `m->procid = runtime·lwp_self();` の追加**:
    *   `runtime·minit()`は、Goランタイムが新しいM(OSスレッド)を初期化するたびに呼び出される関数です。
    *   この行の追加により、各Mが起動する際に、そのMが現在実行されているLWPの実際の`procid`を`runtime·lwp_self()`システムコールを通じて取得し、それを`m->procid`フィールドに格納します。
    *   `m->procid`は、GoランタイムがLWPを識別し、特に`runtime·lwp_unpark()`のようなLWP操作を行う際に必要となる重要な情報です。この動的な取得により、Cgoによって作成されたスレッドや、OSによって異なるLWP IDが割り当てられた場合でも、Goランタイムは常に正しい`procid`を把握し、LWPのアンパークを確実に行えるようになります。これにより、GoプログラムがNetBSD上でCgoと連携する際の安定性が大幅に向上します。

## 関連リンク

*   Go言語の公式リポジトリ: [https://github.com/golang/go](https://github.com/golang/go)
*   GoのCgoに関するドキュメント: [https://go.dev/blog/cgo](https://go.dev/blog/cgo)
*   NetBSDのLWPに関する情報 (一般的な概念): [https://www.netbsd.org/docs/guide/en/chap-threads.html](https://www.netbsd.org/docs/guide/en/chap-threads.html) (これは一般的なガイドであり、特定のシステムコールについては含まれていない可能性があります)

## 参考にした情報源リンク

*   NetBSDのシステムコールに関する情報 (例: `sys/syscall.h` や関連するマニュアルページ):
    *   NetBSDのソースコードリポジトリやオンラインマニュアルページで`sys__lwp_self`を検索すると、より詳細な情報が得られます。例えば、[https://man.netbsd.org/](https://man.netbsd.org/)
*   Goランタイムの内部構造に関する一般的な情報:
    *   Goのソースコード (`src/runtime` ディレクトリ)
    *   Goのスケジューラに関するブログ記事やドキュメント (例: [https://go.dev/doc/effective_go#concurrency](https://go.dev/doc/effective_go#concurrency))
*   Goのコミット履歴とコードレビューシステム (Gerrit):
    *   [https://golang.org/cl/6257071](https://golang.org/cl/6257071) (コミットメッセージに記載されているGerritの変更リストへのリンク)
    *   GerritはGoプロジェクトのコードレビューシステムであり、このコミットに関する議論や背景情報が含まれている可能性があります。
# [インデックス 13217] ファイルの概要

このコミットは、GoランタイムがNetBSD上でLWP (Lightweight Process) の`procid`を適切に初期化するように変更するものです。特に、Cgoを使用する際にLWPのアンパークが正しく機能するために、`procid`が常に`minit()`関数内で初期化されるように修正されています。

## コミット

commit deb93b0f7b646faabc7b4c5db49d7a586a17247e Author: Joel Sing jsing@google.com Date: Thu May 31 03:27:04 2012 +1000

runtime: always initialise procid on netbsd

The correct procid is needed for unparking LWPs on NetBSD - always
initialise procid in minit() so that cgo works correctly. The non-cgo
case already works correctly since procid is initialised via
lwp_create().

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/6257071

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

[https://github.com/golang/go/commit/deb93b0f7b646faabc7b4c5db49d7a586a17247e](https://github.com/golang/go/commit/deb93b0f7b646faabc7b4c5db49d7a586a17247e)

## 元コミット内容

このコミットは、NetBSD環境におけるGoランタイムの動作を改善することを目的としています。具体的には、LWP (Lightweight Process) の`procid`(プロセスIDに相当するLWP識別子)の初期化方法を変更し、特にCgo(GoとC言語の相互運用機能)を使用する際のLWPのアンパーク(休止状態のLWPを再開させる操作)が正しく行われるようにします。

変更前は、非Cgoの場合には`lwp_create()`を通じて`procid`が初期化されていましたが、Cgoを使用する場合には`procid`が正しく設定されない可能性がありました。このコミットでは、`minit()`関数内で常に`runtime·lwp_self()`システムコールを呼び出して現在のLWPの`procid`を取得し、それを`m->procid`に設定することで、この問題を解決しています。これにより、Cgoの有無にかかわらず、LWPのアンパークに必要な正しい`procid`が常に利用可能になります。

## 変更の背景

Goランタイムは、OSのスレッド(NetBSDではLWP)を抽象化してゴルーチンをスケジューリングします。LWPは、GoのM(Machine)構造体に対応し、実際のOSスレッドを表します。Mは、実行中のゴルーチン(G)を管理し、必要に応じてLWPをパーク(休止)またはアンパーク(再開)します。

NetBSDでは、LWPをアンパークする際に、対象となるLWPの正確な識別子(`procid`)が必要です。コミットメッセージによると、Cgoを使用しない通常のGoプログラムでは、LWPが作成される際に`procid`が適切に初期化されていました。しかし、Cgoが絡むシナリオでは、この`procid`が正しく設定されないケースがあり、その結果、LWPのアンパークが失敗し、プログラムのデッドロックや異常終了につながる可能性がありました。

この問題は、GoランタイムがCgoと連携して動作する際の安定性と信頼性に影響を与えるため、`procid`の初期化をより堅牢な方法で行う必要がありました。具体的には、Goランタイムの初期化フェーズである`minit()`において、常に現在のLWPの`procid`を取得し、`m`構造体に格納することで、Cgoの有無にかかわらずLWPのアンパークが確実に機能するようにすることが変更の背景です。

## 前提知識の解説

### NetBSDとLWP (Lightweight Process)

*   **NetBSD**: オープンソースのUNIX系オペレーティングシステムの一つで、高い移植性を特徴としています。様々なハードウェアアーキテクチャで動作します。
*   **LWP (Lightweight Process)**: NetBSDを含む多くのUNIX系OSで採用されているスレッド実装の概念です。LWPはカーネルが管理する実行単位であり、ユーザーレベルのスレッド(Goのゴルーチンなど)がカーネルに処理を依頼する際の基盤となります。一つのプロセスは複数のLWPを持つことができ、各LWPは独立した実行コンテキスト(レジスタ、スタックなど)を持ちます。Goランタイムは、これらのLWPをGoのM(Machine)として利用し、ゴルーチンをスケジューリングします。
*   **`procid`**: NetBSDにおけるLWPの識別子です。LWPを特定し、操作(例えば、アンパーク)するために使用されます。これは、一般的なOSにおけるスレッドIDやプロセスIDに似た概念です。

### GoランタイムのMとG

*   **M (Machine)**: GoランタイムにおけるOSスレッドの抽象化です。各Mは一つのLWP(またはOSスレッド)に対応し、そのLWP上でゴルーチンを実行します。Mは、スケジューラ、スタック、レジスタなどのコンテキストを持ちます。
*   **G (Goroutine)**: Go言語の軽量な並行処理単位です。Goランタイムによって管理され、M上で実行されます。GはOSスレッドよりもはるかに軽量で、数百万個のGを同時に作成することも可能です。
*   **スケジューラ**: Goランタイムの重要なコンポーネントで、GをMに割り当てて実行を管理します。Gがブロックされたり、I/O待ちになったりすると、スケジューラは別のGを同じM上で実行させ、LWPの効率的な利用を促進します。

### Cgo

*   **Cgo**: Go言語の機能の一つで、GoプログラムからC言語のコードを呼び出したり、C言語のコードからGoの関数を呼び出したりすることを可能にします。Cgoを使用すると、既存のCライブラリをGoプロジェクトに統合したり、Goでは実装が難しい低レベルの操作を行ったりすることができます。Cgoは、GoランタイムとCランタイムの間でコンテキストの切り替えを伴うため、スレッド管理やシグナル処理において特別な考慮が必要になることがあります。

### `minit()`と`osinit()`

*   **`runtime·osinit()`**: GoランタイムがOS固有の初期化を行う関数です。この関数は、プログラムの起動時に一度だけ呼び出され、CPU数やその他のOS関連の設定を初期化します。
*   **`runtime·minit()`**: 各M(OSスレッド)が起動する際に呼び出される初期化関数です。この関数は、M固有のリソース(シグナルハンドラ、スタックなど)をセットアップします。

### システムコール (`sys__lwp_self`)

*   **システムコール**: アプリケーションがOSカーネルのサービスを要求するためのインターフェースです。例えば、ファイル操作、メモリ管理、プロセス管理など、OSの機能を利用する際にシステムコールが使用されます。
*   **`sys__lwp_self`**: NetBSDのシステムコールの一つで、現在のLWPの識別子(`procid`)を返します。このシステムコールは、アセンブリ言語で直接呼び出されます。Web検索の結果によると、`sys__lwp_self`のシステムコール番号は`311`です。

## 技術的詳細

このコミットの技術的な核心は、NetBSDにおけるLWPの`procid`の取得と、GoランタイムのM構造体へのその値の格納です。

1.  **`runtime·lwp_self` アセンブリ関数の追加**:
    *   `src/pkg/runtime/sys_netbsd_386.s` (32ビットx86アーキテクチャ用) と `src/pkg/runtime/sys_netbsd_amd64.s` (64ビットx86-64アーキテクチャ用) に、`runtime·lwp_self`という新しいアセンブリ関数が追加されました。
    *   この関数は、NetBSDのシステムコールである`sys__lwp_self`を呼び出します。
    *   `sys__lwp_self`のシステムコール番号は`311`です。
    *   32ビット版 (`sys_netbsd_386.s`) では、システムコール番号を`AX`レジスタにロードし、`INT $0x80`命令でシステムコールを呼び出します。
    *   64ビット版 (`sys_netbsd_amd64.s`) では、システムコール番号を`AX`レジスタにロードし、`SYSCALL`命令でシステムコールを呼び出します。
    *   これらのアセンブリ関数は、現在のLWPの`procid`を戻り値として返します。

2.  **`runtime·thread_netbsd.c` の変更**:
    *   `runtime·lwp_self`関数のプロトタイプ宣言が追加されました: `extern int32 runtime·lwp_self(void);`
    *   **`runtime·osinit()` からの `m->procid = 1;` の削除**:
        *   変更前は、`runtime·osinit()`関数内でメインスレッドの`procid`がハードコードで`1`に設定されていました。これは、メインスレッドのLWP IDが常に`1`であるという仮定に基づいています。しかし、この仮定は常に正しいとは限らず、特にCgoが絡む場合に問題を引き起こす可能性がありました。また、`osinit`はOS全体の初期化であり、個々のMの`procid`を設定する場所としては適切ではありません。
    *   **`runtime·minit()` への `m->procid = runtime·lwp_self();` の追加**:
        *   `runtime·minit()`関数は、各M(Goランタイムが管理するOSスレッド)が初期化される際に呼び出されます。
        *   この変更により、各Mが起動するたびに`runtime·lwp_self()`を呼び出し、そのMが対応するLWPの実際の`procid`を取得して`m->procid`に格納するようになりました。
        *   これにより、各Mは自身のLWPの正確な識別子を常に持つことになり、LWPのアンパークなどの操作が確実に行えるようになります。特にCgoが関与する場合でも、正しい`procid`が保証されるため、LWPのライフサイクル管理が安定します。

この変更により、GoランタイムはNetBSD上でLWPの`procid`を動的に取得し、各Mに割り当てることで、Cgoの有無にかかわらずLWPのアンパーク処理の信頼性を向上させています。

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

### `src/pkg/runtime/sys_netbsd_386.s` および `src/pkg/runtime/sys_netbsd_amd64.s`

```diff
--- a/src/pkg/runtime/sys_netbsd_386.s
+++ b/src/pkg/runtime/sys_netbsd_386.s
@@ -292,6 +292,11 @@ TEXT runtime·lwp_unpark(SB),7,$-4
 	INT	$0x80
 	RET
 
+TEXT runtime·lwp_self(SB),7,$-4
+	MOVL	$311, AX		// sys__lwp_self
+	INT	$0x80
+	RET
+
 TEXT runtime·sysctl(SB),7,$28
 	LEAL	arg0+0(FP), SI
 	LEAL	4(SP), DI
--- a/src/pkg/runtime/sys_netbsd_amd64.s
+++ b/src/pkg/runtime/sys_netbsd_amd64.s
@@ -61,6 +61,11 @@ TEXT runtime·lwp_unpark(SB),7,$0
 	SYSCALL
 	RET
 
+TEXT runtime·lwp_self(SB),7,$0
+	MOVL	$311, AX		// sys__lwp_self
+	SYSCALL
+	RET
+
 // Exit the entire program (like C exit)
 TEXT runtime·exit(SB),7,$-8
 	MOVL	8(SP), DI		// arg 1 - exit status

src/pkg/runtime/thread_netbsd.c

--- a/src/pkg/runtime/thread_netbsd.c
+++ b/src/pkg/runtime/thread_netbsd.c
@@ -28,6 +28,7 @@ extern int32 runtime·lwp_create(UcontextT *context, uintptr flags, void *lwpid)\n extern void runtime·lwp_mcontext_init(void *mc, void *stack, M *m, G *g, void (*fn)(void));\n extern int32 runtime·lwp_park(Timespec *abstime, int32 unpark, void *hint, void *unparkhint);\n extern int32 runtime·lwp_unpark(int32 lwp, void *hint);\n+extern int32 runtime·lwp_self(void);\n \n // From NetBSD\'s <sys/sysctl.h>\n #define\tCTL_HW\t6\n@@ -181,9 +182,6 @@ void\n runtime·osinit(void)\n {\n 	runtime·ncpu = getncpu();\n-\n-\t// Main thread is always LWP 1.\n-\tm->procid = 1;\n }\n \n void\n@@ -196,6 +194,8 @@ void\n runtime·minit(void)\n {\n+\tm->procid = runtime·lwp_self();\n+\n \t// Initialize signal handling\n \tm->gsignal = runtime·malg(32*1024);\n \truntime·signalstack((byte*)m->gsignal->stackguard - StackGuard, 32*1024);\n```

## コアとなるコードの解説

### `runtime·lwp_self` アセンブリ関数

このアセンブリ関数は、現在のLWPの`procid`を取得するためのラッパーです。
*   `MOVL $311, AX`: `AX`レジスタにシステムコール番号`311`をロードします。`311`はNetBSDの`sys__lwp_self`システムコールに対応します。
*   `INT $0x80` (32ビット) または `SYSCALL` (64ビット): システムコールを実行します。これにより、カーネルが`sys__lwp_self`を呼び出し、現在のLWPの`procid`を返します。この戻り値は通常、`AX`レジスタに格納されます。
*   `RET`: 関数から戻ります。`AX`レジスタの値が関数の戻り値となります。

この関数は、GoのランタイムコードからCgoを介さずに直接OSのLWP IDを取得するための低レベルなインターフェースを提供します。

### `src/pkg/runtime/thread_netbsd.c` の変更

1.  **`extern int32 runtime·lwp_self(void);`**:
    *   これは、上記で定義されたアセンブリ関数`runtime·lwp_self`をCコードから呼び出すための前方宣言です。これにより、Cコードがアセンブリで実装された関数を利用できるようになります。

2.  **`runtime·osinit()` からの `m->procid = 1;` の削除**:
    *   以前は、GoランタイムのOS初期化フェーズで、メインスレッドの`procid`が静的に`1`に設定されていました。これは、多くのUNIX系システムでメインスレッドのIDが`1`であることが多いためですが、NetBSDのLWP IDの割り当てが常にこの仮定に従うとは限りません。特に、Cgoが介入するような複雑なシナリオでは、この静的な設定が問題を引き起こす可能性がありました。この行を削除することで、`procid`の初期化をより動的かつ正確な方法に委ねます。

3.  **`runtime·minit()` への `m->procid = runtime·lwp_self();` の追加**:
    *   `runtime·minit()`は、Goランタイムが新しいM(OSスレッド)を初期化するたびに呼び出される関数です。
    *   この行の追加により、各Mが起動する際に、そのMが現在実行されているLWPの実際の`procid`を`runtime·lwp_self()`システムコールを通じて取得し、それを`m->procid`フィールドに格納します。
    *   `m->procid`は、GoランタイムがLWPを識別し、特に`runtime·lwp_unpark()`のようなLWP操作を行う際に必要となる重要な情報です。この動的な取得により、Cgoによって作成されたスレッドや、OSによって異なるLWP IDが割り当てられた場合でも、Goランタイムは常に正しい`procid`を把握し、LWPのアンパークを確実に行えるようになります。これにより、GoプログラムがNetBSD上でCgoと連携する際の安定性が大幅に向上します。

## 関連リンク

*   Go言語の公式リポジトリ: [https://github.com/golang/go](https://github.com/golang/go)
*   GoのCgoに関するドキュメント: [https://go.dev/blog/cgo](https://go.dev/blog/cgo)
*   NetBSDのLWPに関する情報 (一般的な概念): [https://www.netbsd.org/docs/guide/en/chap-threads.html](https://www.netbsd.org/docs/guide/en/chap-threads.html) (これは一般的なガイドであり、特定のシステムコールについては含まれていない可能性があります)

## 参考にした情報源リンク

*   NetBSDのシステムコールに関する情報 (例: `sys/syscall.h` や関連するマニュアルページ):
    *   NetBSDのソースコードリポジトリやオンラインマニュアルページで`sys__lwp_self`を検索すると、より詳細な情報が得られます。例えば、[https://man.netbsd.org/](https://man.netbsd.org/)
*   Goランタイムの内部構造に関する一般的な情報:
    *   Goのソースコード (`src/runtime` ディレクトリ)
    *   Goのスケジューラに関するブログ記事やドキュメント (例: [https://go.dev/doc/effective_go#concurrency](https://go.dev/doc/effective_go#concurrency))
*   Goのコミット履歴とコードレビューシステム (Gerrit):
    *   [https://golang.org/cl/6257071](https://golang.org/cl/6257071) (コミットメッセージに記載されているGerritの変更リストへのリンク)
    *   GerritはGoプロジェクトのコードレビューシステムであり、このコミットに関する議論や背景情報が含まれている可能性があります。
*   `sys__lwp_self` システムコール番号に関する情報:
    *   [https://www.cvut.cz/](https://www.cvut.cz/)
    *   [https://googlesource.com/](https://googlesource.com/)
    *   [https://h-da.de/](https://h-da.de/)