[インデックス 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のamd64
と386
アーキテクチャ向けのネットワークポーラーを統合する。
変更の背景
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
をネイティブに利用できるようにすることです。これには以下の技術的な変更が含まれます。
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
にイベントを登録したり、イベントの種類を識別したりするために必要です。EINTR
とEFAULT
エラーコードの定義も、既存の定義箇所から移動または再定義されています。これは、kqueue
システムコールがこれらのエラーを返す可能性があるため、ランタイムが適切に処理できるようにするためです。
Kevent
構造体の定義:kqueue
システムコールは、イベントの登録や取得にkevent
構造体を使用します。このコミットでは、GoランタイムがC言語のstruct kevent
に対応するKevent
型を定義し、Goコードからkqueue
システムコールに渡せるようにしています。これにより、イベントの識別子(ident
)、フィルター(filter
)、フラグ(flags
)、データ(data
)などをGo側で操作できるようになります。
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ランタイムが直接
これらの変更により、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
システムコールを直接利用できるようにするための基盤を構築している点です。
-
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
システムコールとやり取りできるようになります。
- これらのファイルは、GoランタイムがOS固有の定数、構造体、およびシステムコール番号を認識するために使用されます。
-
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 Change List (CL) 8825043: https://golang.org/cl/8825043
- Go言語のネットワークポーラーに関する一般的な情報:
- Goのnetpollerの仕組み (Medium): https://medium.com/@vladimir.vivien/go-s-netpoller-demystified-1e5e2082018e
- GoのI/O多重化 (morsmachine.dk): https://morsmachine.dk/go-io-multiplexing
参考にした情報源リンク
- 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のドキュメントや関連ブログ記事)