[インデックス 17109] ファイルの概要
このコミットは、Go言語のnet
パッケージにおけるファイルディスクリプタのポーリングメカニズムに関する変更です。具体的には、Unix系システムにおけるポーラー(pollster
)の初期化と、ネットワークファイルディスクリプタの割り当てを分離しています。これにより、将来的にBSD系のシステムでランタイム統合型のネットワークポーラーへの移行をスムーズに行うための準備となります。
コミット
commit 554d47ecb5f35dd5bb850f5e20dde978bea37061
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date: Fri Aug 9 09:02:27 2013 +0900
net: separate unix pollster initialization from network file descriptor allocation
Unlike the existing net package own pollster, runtime-integrated
network pollster on BSD variants, actually kqueue, requires a socket
that has beed passed to syscall.Listen previously for a stream
listener.
This CL separates pollDesc.Init of Unix network pollster from newFD
to avoid any breakages in the transition from Unix network pollster
to runtime-integrated pollster. Upcoming CLs will rearrange the call
order of pollster and syscall functions like the following;
- For dialers that open active connections, pollDesc.Init will be
called in between syscall.Bind and syscall.Connect.
- For stream listeners that open passive stream connections,
pollDesc.Init will be called just after syscall.Listen.
- For datagram listeners that open datagram connections,
pollDesc.Init will be called just after syscall.Bind.
This is in preparation for runtime-integrated network pollster for BSD
variants.
Update #5199
R=dvyukov, bradfitz
CC=golang-dev
https://golang.org/cl/12663043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/554d47ecb5f35dd5bb850f5e20dde978bea37061
元コミット内容
net: separate unix pollster initialization from network file descriptor allocation
このコミットは、Unix系システムにおけるネットワークポーラーの初期化と、ネットワークファイルディスクリプタの割り当てを分離することを目的としています。
変更の背景
Go言語のnet
パッケージは、ネットワークI/Oを効率的に処理するために、内部的にポーリングメカニズムを使用しています。既存のnet
パッケージ独自のポーラーとは異なり、BSD系OS(FreeBSD, OpenBSD, macOSなど)で利用されるkqueue
のようなランタイム統合型のネットワークポーラーは、ストリームリスナーの場合、syscall.Listen
によって事前にソケットが渡されている必要があります。
このコミット(Change List, CL)は、Unixネットワークポーラーからランタイム統合ポーラーへの移行時に発生しうる不具合を避けるため、pollDesc.Init
の呼び出しをnewFD
(新しいファイルディスクリプタの割り当て)から分離します。
今後のコミットでは、ポーラーとシステムコール関数の呼び出し順序が以下のように再編成される予定です。
- アクティブな接続を開くダイアラーの場合:
syscall.Bind
とsyscall.Connect
の間にpollDesc.Init
が呼び出されます。 - パッシブなストリーム接続を開くストリームリスナーの場合:
syscall.Listen
の直後にpollDesc.Init
が呼び出されます。 - データグラム接続を開くデータグラムリスナーの場合:
syscall.Bind
の直後にpollDesc.Init
が呼び出されます。
この変更は、BSD系OS向けのランタイム統合型ネットワークポーラーの準備段階として行われています。
前提知識の解説
1. ファイルディスクリプタ (File Descriptor, FD)
Unix系OSにおいて、ファイルやソケットなどのI/Oリソースを識別するためにカーネルが割り当てる非負の整数です。プログラムはファイルディスクリプタを通じてこれらのリソースにアクセスします。
2. ポーリング (Polling)
複数のI/O操作(ネットワーク通信、ファイルI/Oなど)の完了を待機するためのメカニズムです。プログラムは、I/O操作が完了したかどうかを定期的に確認(ポーリング)したり、I/Oイベントが発生した際にカーネルから通知を受け取ったりします。
3. ネットワークポーラー (Network Pollster)
Go言語のnet
パッケージにおいて、ネットワークI/Oの非同期処理を管理するコンポーネントです。ソケットの読み書きが可能になったり、接続が確立されたりといったイベントを監視し、それに応じてGoルーチンをスケジューリングします。これにより、多数の同時接続を効率的に処理できます。
4. pollDesc
Goのnet
パッケージ内部で使用される構造体で、個々のファイルディスクリプタ(ソケットなど)に関連付けられたポーリングの状態を管理します。pollDesc.Init
は、このポーリング記述子を初期化し、ポーラーに登録する役割を担います。
5. kqueue
BSD系OSで利用される、高性能なイベント通知インターフェースです。ファイルディスクリプタの状態変化(読み込み可能、書き込み可能、エラーなど)を効率的に監視し、イベントが発生した際にアプリケーションに通知します。Linuxのepoll
やWindowsのI/O Completion Ports (IOCP) に相当するものです。
6. syscall.Listen
, syscall.Bind
, syscall.Connect
これらはGoのsyscall
パッケージに含まれる関数で、OSのシステムコールを直接呼び出します。
syscall.Listen
: ソケットをリッスン状態にし、着信接続を受け入れる準備をします。syscall.Bind
: ソケットにローカルアドレス(IPアドレスとポート番号)を割り当てます。syscall.Connect
: リモートアドレスに接続を確立します。
7. ランタイム統合型ネットワークポーラー
Goのランタイム(実行環境)に直接統合されたネットワークポーラーを指します。これにより、Goのスケジューラとポーラーが密接に連携し、より効率的なI/O処理とGoルーチンのスケジューリングが可能になります。特に、OSが提供する高性能なイベント通知メカニズム(例: kqueue
)を最大限に活用することで、スケーラビリティとパフォーマンスが向上します。
技術的詳細
このコミットの核心は、pollDesc
の初期化タイミングの変更にあります。これまでは、新しいファイルディスクリプタが作成されるnewFD
の過程でpollDesc.Init
が呼び出されていた可能性があります。しかし、BSD系のkqueue
のようなランタイム統合ポーラーは、ソケットが特定の状態(例えば、syscall.Listen
によってリッスン状態になっている)になってからポーラーに登録されることを要求します。
このコミットでは、src/pkg/net/fd_poll_unix.go
内のpollDesc
のメソッド(Lock
, Unlock
, Wakeup
, Evict
)に、pd.pollServer == nil
というnilチェックが追加されています。これは、pollDesc.pollServer
がまだ初期化されていない(つまり、pollDesc.Init
が呼び出されていない)場合に、これらの操作が安全にスキップされるようにするためです。
これにより、pollDesc.Init
の呼び出しがnewFD
から分離され、将来的にネットワーク操作のライフサイクルにおけるより適切なタイミングで呼び出されるようになります。具体的には、ソケットが完全に設定され、OSのシステムコールによって準備が整った後にポーラーに登録されることで、ランタイム統合ポーラーの要件を満たし、より堅牢なネットワークI/O処理を実現します。
この変更は、Goのネットワークスタックの低レベルな部分に影響を与え、特にBSD系OSでのパフォーマンスと安定性の向上に寄与します。
コアとなるコードの変更箇所
src/pkg/net/fd_poll_unix.go
--- a/src/pkg/net/fd_poll_unix.go
+++ b/src/pkg/net/fd_poll_unix.go
@@ -252,14 +252,23 @@ func (pd *pollDesc) Close() {
}
func (pd *pollDesc) Lock() {
+ if pd.pollServer == nil {
+ return
+ }
pd.pollServer.Lock()
}
func (pd *pollDesc) Unlock() {
+ if pd.pollServer == nil {
+ return
+ }
pd.pollServer.Unlock()
}
func (pd *pollDesc) Wakeup() {
+ if pd.pollServer == nil {
+ return
+ }
pd.pollServer.Wakeup()
}
@@ -294,6 +303,9 @@ func (pd *pollDesc) WaitWrite() error {
}
func (pd *pollDesc) Evict() bool {
+ if pd.pollServer == nil {
+ return false
+ }
return pd.pollServer.Evict(pd)
}
コアとなるコードの解説
変更は、pollDesc
構造体の以下のメソッドにpd.pollServer == nil
というnilチェックを追加しています。
Lock()
Unlock()
Wakeup()
Evict()
これらのメソッドは、pollDesc
が管理するポーラーサーバー(pd.pollServer
)に対して操作を行います。pd.pollServer
はpollDesc.Init
が呼び出された際に初期化されます。
変更前は、これらのメソッドが呼び出された時点でpd.pollServer
が必ず初期化されていることを前提としていました。しかし、pollDesc.Init
の呼び出しタイミングをnewFD
から分離することで、これらのメソッドがpd.pollServer
がまだnilの状態で呼び出される可能性が出てきます。
このnilチェックを追加することで、pd.pollServer
がまだ設定されていない場合でも、これらのメソッドがパニックを起こすことなく安全に実行をスキップできるようになります。これは、pollDesc.Init
がより遅い段階で、かつ適切なコンテキストで呼び出されるようになるための準備であり、Goのネットワークスタックの堅牢性を高めるための重要な変更です。
関連リンク
- Go Issue 5199:
net: runtime-integrated network pollster for BSD variants
- このコミットが解決または関連するGoのIssueです。 - Go CL 12663043:
net: separate unix pollster initialization from network file descriptor allocation
- このコミットのChange Listページです。
参考にした情報源リンク
- Go言語のソースコード (
src/pkg/net/fd_poll_unix.go
) - Go言語のIssueトラッカー (Issue 5199)
- Go言語のChange List (CL 12663043)
- Unix系OSのシステムコールに関する一般的な知識 (e.g.,
listen
,bind
,connect
) kqueue
に関する一般的な知識 (BSD系OSのイベント通知メカニズム)- Go言語のネットワークプログラミングに関する一般的な知識