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

[インデックス 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.Bindsyscall.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.pollServerpollDesc.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言語のネットワークプログラミングに関する一般的な知識