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

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

このコミットは、GoランタイムにおけるFreeBSDおよびOpenBSDのamd64および386アーキテクチャ向けネットワークポーラーの統合に関するものです。具体的には、これらのOSで効率的なI/O多重化を実現するためのkqueueシステムコール関連の定義とアセンブリコードが追加されています。

コミット

commit c5732c8526fa4fef697730a739631fd69cf4f965
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date:   Mon May 20 19:25:32 2013 +0900

    runtime: integrated network poller for freebsd/amd64,386, openbsd/amd64,386
    
    Update #5199
    
    R=golang-dev, dvyukov
    CC=golang-dev
    https://golang.org/cl/8825043

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

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

元コミット内容

Goランタイムに、FreeBSDおよびOpenBSDのamd64386アーキテクチャ向けのネットワークポーラーを統合する。

変更の背景

Go言語のランタイムは、多数の同時接続を効率的に処理するためにネットワークポーラーを利用しています。これは、Goの並行処理モデル(ゴルーチン)と非同期I/Oを組み合わせる上で不可欠な要素です。Linuxではepoll、WindowsではIOCPが使用されるように、BSD系のOS(FreeBSD, OpenBSD, macOSなど)ではkqueueが最も効率的なI/O多重化メカニズムとして知られています。

このコミット以前は、FreeBSDやOpenBSDにおけるGoランタイムのネットワークI/O処理が最適化されていなかった可能性があります。kqueueを統合することで、これらのプラットフォーム上でのGoアプリケーションのネットワークパフォーマンスとスケーラビリティを向上させることが目的です。コミットメッセージにあるUpdate #5199は、この機能追加に関連する内部の課題トラッカーの番号であると考えられます。

前提知識の解説

  • ネットワークポーラー (Network Poller): オペレーティングシステムが提供するI/O多重化メカニズム(例: select, poll, epoll, kqueue)を利用して、複数のファイルディスクリプタ(ソケットなど)からのI/Oイベントを効率的に監視し、準備ができたものだけをアプリケーションに通知するコンポーネントです。これにより、多数の接続を扱うサーバーアプリケーションなどで、スレッド数を抑えつつ高い並行性を実現できます。
  • kqueue: FreeBSD、OpenBSD、macOS、DragonFly BSDなどのBSD系OSで利用される、高性能なイベント通知インターフェースです。ファイルディスクリプタのI/Oイベントだけでなく、タイマー、シグナル、プロセス状態の変化など、様々なカーネルイベントを監視できます。kqueueは、イベントの登録、変更、削除、および発生したイベントの取得を効率的に行うためのシステムコール群を提供します。
  • システムコール (System Call): オペレーティングシステムのカーネルが提供するサービスを、ユーザー空間のプログラムから利用するためのインターフェースです。ファイル操作、ネットワーク通信、メモリ管理など、OSの機能にアクセスする際に使用されます。Goランタイムは、OS固有のシステムコールを直接呼び出すことで、低レベルな操作やパフォーマンス最適化を実現しています。
  • アセンブリ言語 (Assembly Language): コンピュータのプロセッサが直接実行できる機械語と1対1に対応する低レベルプログラミング言語です。OSのカーネルやランタイムなど、パフォーマンスが非常に重要で、ハードウェアに密接に連携する部分で利用されることがあります。Goランタイムでは、特定のシステムコールを呼び出すためのラッパー関数や、コンテキストスイッチなどの低レベルな処理にアセンブリ言語が用いられます。
  • Goランタイム (Go Runtime): Goプログラムの実行を管理する部分です。ゴルーチンのスケジューリング、ガベージコレクション、ネットワークI/Oの処理、システムコールへの橋渡しなど、Goプログラムが効率的かつ安全に動作するための様々な機能を提供します。Goの「ブロッキングI/O」は、内部的には非ブロッキングI/Oとネットワークポーラーによって実現されています。

技術的詳細

このコミットの主要な目的は、GoランタイムがFreeBSDおよびOpenBSD上でkqueueをネイティブに利用できるようにすることです。これには以下の技術的な変更が含まれます。

  1. kqueue関連定数の追加:
    • src/pkg/runtime/defs_freebsd.go, src/pkg/runtime/defs_openbsd.go および対応するアーキテクチャ固有のヘッダーファイル (defs_freebsd_386.h, defs_freebsd_amd64.h, defs_openbsd_386.h, defs_openbsd_amd64.h) に、kqueueシステムコールで使用される定数(EV_ADD, EV_DELETE, EV_CLEAR, EV_RECEIPT, EV_ERROR, EVFILT_READ, EVFILT_WRITEなど)が追加されています。これらの定数は、kqueueにイベントを登録したり、イベントの種類を識別したりするために必要です。
    • EINTREFAULTエラーコードの定義も、既存の定義箇所から移動または再定義されています。これは、kqueueシステムコールがこれらのエラーを返す可能性があるため、ランタイムが適切に処理できるようにするためです。
  2. Kevent構造体の定義:
    • kqueueシステムコールは、イベントの登録や取得にkevent構造体を使用します。このコミットでは、GoランタイムがC言語のstruct keventに対応するKevent型を定義し、Goコードからkqueueシステムコールに渡せるようにしています。これにより、イベントの識別子(ident)、フィルター(filter)、フラグ(flags)、データ(data)などをGo側で操作できるようになります。
  3. kqueueシステムコールのアセンブリラッパー:
    • Goランタイムが直接kqueueシステムコールを呼び出すためのアセンブリ言語によるラッパー関数が追加されています。具体的には、src/pkg/runtime/sys_freebsd_386.s, src/pkg/runtime/sys_freebsd_amd64.s, src/pkg/runtime/sys_openbsd_386.s, src/pkg/runtime/sys_openbsd_amd64.sに以下の関数が追加されました。
      • runtime·kqueue: kqueueインスタンスを作成するためのシステムコールを呼び出します。
      • runtime·kevent: kqueueインスタンスにイベントを登録したり、発生したイベントを取得したりするためのシステムコールを呼び出します。
      • runtime·closeonexec: ファイルディスクリプタにFD_CLOEXECフラグを設定するためのfcntlシステムコールを呼び出します。これは、fork後に子プロセスでファイルディスクリプタが自動的に閉じられるようにするためのセキュリティおよびリソース管理のベストプラクティスです。ネットワークポーラーが使用するファイルディスクリプタが意図せず子プロセスに継承されるのを防ぎます。

これらの変更により、GoランタイムはFreeBSDおよびOpenBSD上でkqueueを基盤とした効率的なネットワークポーラーを構築できるようになり、GoプログラムのネットワークI/O性能が向上します。

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

このコミットでは、主に以下のファイルが変更されています。

  • src/pkg/runtime/defs_freebsd.go
  • src/pkg/runtime/defs_freebsd_386.h
  • src/pkg/runtime/defs_freebsd_amd64.h
  • src/pkg/runtime/defs_openbsd.go
  • src/pkg/runtime/defs_openbsd_386.h
  • src/pkg/runtime/defs_openbsd_amd64.h
  • src/pkg/runtime/sys_freebsd_386.s
  • src/pkg/runtime/sys_freebsd_amd64.s
  • src/pkg/runtime/sys_openbsd_386.s
  • src/pkg/runtime/sys_openbsd_amd64.s

これらのファイルは、Goランタイムが特定のOSおよびアーキテクチャと連携するための低レベルな定義やシステムコールラッパーを含んでいます。

具体的な変更例(src/pkg/runtime/defs_freebsd.goより抜粋):

--- a/src/pkg/runtime/defs_freebsd.go
+++ b/src/pkg/runtime/defs_freebsd.go
@@ -19,6 +19,7 @@ package runtime
 #include <sys/time.h>
 #include <signal.h>
 #include <errno.h>
+#include <sys/event.h> // kqueue関連のヘッダーを追加
 #include <sys/mman.h>
 #include <sys/ucontext.h>
 #include <sys/umtx.h>
@@ -30,6 +31,9 @@ package runtime
 import "C"
 
 const (
+// EINTRとEFAULTの再定義(または追加)
+\tEINTR  = C.EINTR
+\tEFAULT = C.EFAULT
+\
 \tPROT_NONE  = C.PROT_NONE
 \tPROT_READ  = C.PROT_READ
 \tPROT_WRITE = C.PROT_WRITE
@@ -48,8 +52,6 @@ const (
 \tUMTX_OP_WAIT_UINT = C.UMTX_OP_WAIT_UINT
 \tUMTX_OP_WAKE      = C.UMTX_OP_WAKE
 \
-\tEINTR = C.EINTR // 既存のEINTR定義を削除
-\
 \tSIGHUP    = C.SIGHUP
 \tSIGINT    = C.SIGINT
 \tSIGQUIT   = C.SIGQUIT
@@ -101,6 +103,14 @@ const (
 \tITIMER_REAL    = C.ITIMER_REAL
 \tITIMER_VIRTUAL = C.ITIMER_VIRTUAL
 \tITIMER_PROF    = C.ITIMER_PROF
+\
+// kqueue関連の定数を追加
+\tEV_ADD       = C.EV_ADD
+\tEV_DELETE    = C.EV_DELETE
+\tEV_CLEAR     = C.EV_CLEAR
+\tEV_RECEIPT   = C.EV_RECEIPT
+\tEV_ERROR     = C.EV_ERROR
+\tEVFILT_READ  = C.EVFILT_READ
+\tEVFILT_WRITE = C.EVFILT_WRITE
 )
 
 type Rtprio C.struct_rtprio
@@ -117,3 +127,5 @@ type Ucontext C.ucontext_t
 type Timespec C.struct_timespec
 type Timeval C.struct_timeval
 type Itimerval C.struct_itimerval
+\
+type Kevent C.struct_kevent // Kevent構造体のGo型定義を追加

具体的な変更例(src/pkg/runtime/sys_freebsd_amd64.sより抜粋):

--- a/src/pkg/runtime/sys_freebsd_amd64.s
+++ b/src/pkg/runtime/sys_freebsd_amd64.s
@@ -280,3 +280,37 @@ TEXT runtime·sigprocmask(SB),7,$0
 \tJAE\t2(PC)\n \tMOVL\t$0xf1, 0xf1  // crash\n \tRET\n+\n+// int32 runtime·kqueue(void);\n+TEXT runtime·kqueue(SB),7,$0
+\tMOVQ\t$0, DI
+\tMOVQ\t$0, SI
+\tMOVQ\t$0, DX
+\tMOVL\t$362, AX // kqueueシステムコール番号
+\tSYSCALL
+\tJCC\t2(PC)
+\tNEGQ\tAX
+\tRET
+\n+// int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout);\n+TEXT runtime·kevent(SB),7,$0
+\tMOVL\t8(SP), DI // 引数をレジスタにロード
+\tMOVQ\t16(SP), SI
+\tMOVL\t24(SP), DX
+\tMOVQ\t32(SP), R10
+\tMOVL\t40(SP), R8
+\tMOVQ\t48(SP), R9
+\tMOVL\t$363, AX // keventシステムコール番号
+\tSYSCALL
+\tJCC\t2(PC)
+\tNEGQ\tAX
+\tRET
+\n+// void runtime·closeonexec(int32 fd);\n+TEXT runtime·closeonexec(SB),7,$0
+\tMOVL\t8(SP), DI\t// fd
+\tMOVQ\t$2, SI\t\t// F_SETFD
+\tMOVQ\t$1, DX\t\t// FD_CLOEXEC
+\tMOVL\t$92, AX\t\t// fcntlシステムコール番号
+\tSYSCALL
+\tRET

コアとなるコードの解説

このコミットのコアとなる変更は、GoランタイムがFreeBSDおよびOpenBSD上でkqueueシステムコールを直接利用できるようにするための基盤を構築している点です。

  1. defs_*.goおよびdefs_*.hファイル群:

    • これらのファイルは、GoランタイムがOS固有の定数、構造体、およびシステムコール番号を認識するために使用されます。#include <sys/event.h>の追加は、kqueue関連の定義をGoランタイムにインポートするためのCgoディレクティブです。
    • constブロックに追加されたEV_ADDなどの定数は、kqueueにイベントを登録する際の操作やイベントの種類を指定するためにGoコードから参照されます。
    • type Kevent C.struct_keventは、C言語のstruct keventをGoの型システムにマッピングし、Goのコードからkevent構造体を安全に操作できるようにします。これにより、Goのネットワークポーラー実装(src/runtime/netpoll_kqueue.goなど)が、このKevent型を使ってkqueueシステムコールとやり取りできるようになります。
  2. sys_*.sアセンブリファイル群:

    • これらのファイルには、Goランタイムが直接OSのシステムコールを呼び出すためのアセンブリ言語のラッパー関数が含まれています。
    • TEXT runtime·kqueue(SB),7,$0は、Goのruntimeパッケージ内でkqueueという名前の関数を定義しています。この関数は、kqueueシステムコール(FreeBSD/amd64ではシステムコール番号362、OpenBSD/amd64では269など)を呼び出し、新しいkqueueインスタンスのファイルディスクリプタを返します。
    • TEXT runtime·kevent(SB),7,$0は、keventシステムコールを呼び出すためのラッパーです。この関数は、kqueueインスタンスのファイルディスクリプタ、変更リスト、イベントリスト、タイムアウトなどの引数を受け取り、kqueueにイベントを登録したり、発生したイベントを取得したりします。
    • TEXT runtime·closeonexec(SB),7,$0は、ファイルディスクリプタにFD_CLOEXECフラグを設定するためのfcntlシステムコールを呼び出します。これは、kqueueによって作成されたファイルディスクリプタが、forkされた子プロセスに誤って継承されないようにするために重要です。

これらの低レベルな変更により、GoランタイムはFreeBSDおよびOpenBSDのカーネルと直接連携し、kqueueの高性能なイベント通知メカニズムを最大限に活用できるようになります。これにより、Goのネットワークアプリケーションは、これらのプラットフォーム上でより効率的に動作し、高いスループットと低いレイテンシを実現できるようになります。

関連リンク

参考にした情報源リンク

  • Go runtime network poller kqueue FreeBSD OpenBSDに関するWeb検索結果
  • Go issue 5199に関するWeb検索結果 (直接的な一致は見つからず、内部的な課題トラッカーの可能性が高い)
  • Go言語のソースコード (src/runtime/netpoll_kqueue.goなど、kqueueの実装が利用されるファイル)
  • BSD系OSのkqueueシステムコールに関するドキュメント (例: FreeBSD manページ kqueue(2), kevent(2))
  • Go言語のアセンブリ言語の慣習に関する情報 (Goのドキュメントや関連ブログ記事)