[インデックス 17338] ファイルの概要
このコミットは、GoランタイムにおけるFreeBSD/ARMアーキテクチャ向けのネットワークポーラーの統合に関するものです。具体的には、FreeBSDシステムコールであるkqueue
を利用して、ネットワークI/Oの効率的な監視を可能にするための変更が加えられています。これにより、FreeBSD/ARM環境でのGoプログラムのネットワークパフォーマンスが向上し、より効率的な非同期I/O処理が実現されます。
コミット
commit e82614e5bea67448ffed0cb5146df61f176325b2
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date: Tue Aug 20 16:57:30 2013 +0900
runtime: integrated network pollster for freebsd/arm
Update #6146
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/12927047
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e82614e5bea67448ffed0cb5146df61f176325b2
元コミット内容
runtime: integrated network pollster for freebsd/arm
Update #6146
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/12927047
変更の背景
Goランタイムは、効率的な並行処理とネットワークI/Oを実現するために、オペレーティングシステム(OS)が提供する非同期I/Oメカニズムを積極的に利用しています。Linuxではepoll
、macOSやFreeBSDではkqueue
、WindowsではI/O完了ポート(IOCP)などがこれに該当します。
このコミットが行われた2013年当時、Goは様々なプラットフォームへの対応を進めていました。FreeBSD/ARMアーキテクチャは、組み込みシステムや特定のサーバー環境で利用されることがあり、Goプログラムがこれらの環境でネイティブなパフォーマンスを発揮するためには、OS固有のネットワークポーリングメカニズムへの対応が不可欠でした。
以前のFreeBSD/ARM環境では、おそらく汎用的なポーリングメカニズム(例えば、select
やpoll
のような、より効率の悪いもの)が使用されていたか、あるいはネットワークポーリング機能自体が十分に最適化されていなかった可能性があります。このコミットは、FreeBSD/ARM環境において、より高性能なkqueue
ベースのネットワークポーラーを統合することで、Goのgoroutineスケジューラがネットワークイベントを効率的に処理し、スケーラブルなネットワークアプリケーションを構築できるようにすることを目的としています。
Update #6146
という記述から、この変更が特定の課題やバグ報告に対応するものであることが示唆されますが、現時点ではその具体的な内容を特定することはできませんでした。しかし、一般的にこのような変更は、パフォーマンスのボトルネック解消、リソース利用効率の改善、または特定のプラットフォームでの機能不足の補完を目的としています。
前提知識の解説
ネットワークポーラー (Network Pollster)
Goランタイムにおけるネットワークポーラーは、Goの非同期ネットワークI/Oの心臓部です。Goのgoroutineは軽量なスレッドであり、多数のgoroutineが同時にネットワークI/Oを待機することが一般的です。もし各goroutineがブロッキングI/Oを行うと、OSのスレッドが多数必要となり、コンテキストスイッチのオーバーヘッドが増大します。
ネットワークポーラーは、この問題を解決するために、OSが提供する非同期I/Oイベント通知メカニズム(例: epoll
, kqueue
, IOCP)を利用します。これにより、Goランタイムは少数のOSスレッドで多数のネットワーク接続を効率的に監視し、I/O準備ができたgoroutineのみをスケジューリングすることができます。
具体的には、ネットワークポーラーは以下の役割を担います。
- イベント登録: ネットワークソケット(ファイルディスクリプタ)をOSのイベント通知メカニズムに登録し、読み書き可能になった際に通知を受け取るように設定します。
- イベント待機: OSのイベント通知メカニズムからのイベントを効率的に待機します。
- イベント処理: イベントが発生した際に、対応するgoroutineを「実行可能」状態にし、Goスケジューラに引き渡します。
kqueue
kqueue
は、FreeBSD、macOS、NetBSD、OpenBSDなどのBSD系OSで利用される、高性能なイベント通知インターフェースです。これは、ファイルディスクリプタ(ソケット、ファイル、パイプなど)の状態変化、プロセスイベント、タイマーイベントなど、様々な種類のイベントを効率的に監視するために設計されています。
kqueue
の基本的な概念は以下の通りです。
- kqueueディスクリプタ:
kqueue()
システムコールによって作成される、イベントキューへの参照です。 - kevent構造体: 監視したいイベントの種類(読み込み可能、書き込み可能など)、対象のファイルディスクリプタ、イベント発生時のフラグなどを定義する構造体です。
kevent()
システムコール:kevent()
システムコールは、以下の2つの主要な機能を提供します。- イベントの登録/変更/削除:
changelist
引数を通じて、監視対象のイベントをkqueue
に登録、変更、または削除します。 - イベントの取得:
eventlist
引数を通じて、発生したイベントを取得します。イベントがない場合は、指定されたタイムアウト期間待機します。
- イベントの登録/変更/削除:
kqueue
は、イベント駆動型プログラミングにおいて、select
やpoll
といった古いメカニズムに比べて、よりスケーラブルで効率的なI/O多重化を実現します。特に、多数のファイルディスクリプタを扱う場合にその性能が際立ちます。
ARMアーキテクチャ
ARM(Advanced RISC Machine)は、主にモバイルデバイスや組み込みシステムで広く使用されているRISC(Reduced Instruction Set Computer)ベースのプロセッサアーキテクチャです。低消費電力と高い性能効率が特徴であり、近年ではサーバーやデスクトップPCにも採用が拡大しています。
Go言語は、クロスコンパイル機能が強力であり、様々なアーキテクチャに対応しています。FreeBSD/ARMは、特定のネットワーク機器やストレージシステムなどで利用されることがあり、Goがこれらの環境で動作するためには、そのアーキテクチャ固有のシステムコールやレジスタの扱いに対応する必要があります。
技術的詳細
このコミットは、FreeBSD/ARM環境におけるGoランタイムのネットワークポーリング機能をkqueue
ベースに移行するための具体的な実装変更を含んでいます。
src/pkg/runtime/defs_freebsd_arm.h
の変更
このファイルは、FreeBSD/ARMアーキテクチャ固有の定数や構造体の定義を含んでいます。
EINTR
とEFAULT
のエラーコードが再配置され、EFAULT
が新しく追加されています。これは、システムコールからのエラーハンドリングに関連する可能性があります。kqueue
システムコールで使用されるイベントフラグ(EV_ADD
,EV_DELETE
,EV_CLEAR
,EV_RECEIPT
,EV_ERROR
)とイベントフィルター(EVFILT_READ
,EVFILT_WRITE
)が追加されています。これらはkevent
システムコールに渡すkevent
構造体のflags
やfilter
フィールドで使用されます。Kevent
構造体の定義が追加されています。この構造体はkevent
システムコールでイベントの登録や取得に使用されるもので、ident
(ファイルディスクリプタなど)、filter
(イベントの種類)、flags
(イベントの動作)、fflags
、data
、udata
(ユーザーデータ)などのフィールドを持ちます。この構造体の追加は、kqueue
ベースのポーリングを実装するために不可欠です。
src/pkg/runtime/netpoll.goc
の変更
このファイルは、Goランタイムのネットワークポーリングのビルドタグを定義しています。
// +build darwin freebsd,amd64 freebsd,386 linux netbsd openbsd windows
から// +build darwin freebsd linux netbsd openbsd windows
へ変更されています。 これは、freebsd,amd64
とfreebsd,386
という特定のアーキテクチャ指定を削除し、単にfreebsd
とすることで、FreeBSDのすべてのアーキテクチャ(ARMを含む)でこのnetpoll.goc
がビルドされるように変更されたことを意味します。これにより、FreeBSD/ARMでも共通のネットワークポーリングロジックが利用されるようになります。
src/pkg/runtime/netpoll_kqueue.c
の変更
このファイルは、kqueue
ベースのネットワークポーリングの実装を含んでいます。
// +build darwin freebsd,amd64 freebsd,386 netbsd openbsd
から// +build darwin freebsd netbsd openbsd
へ変更されています。netpoll.goc
と同様に、FreeBSDの特定のアーキテクチャ指定を削除し、FreeBSDのすべてのアーキテクチャでkqueue
ベースのポーリングが使用されるように変更されています。これにより、FreeBSD/ARMもkqueue
を利用したポーリングの恩恵を受けられるようになります。
src/pkg/runtime/netpoll_stub.c
の変更
このファイルは、ネットワークポーリングがサポートされていないプラットフォーム向けのスタブ実装を含んでいます。
// +build freebsd,arm plan9
から// +build plan9
へ変更されています。 これは、FreeBSD/ARMがネットワークポーリングのスタブ実装ではなく、正式なkqueue
ベースの実装を使用するようになったことを明確に示しています。つまり、FreeBSD/ARMはもはや「ポーリング未サポート」のカテゴリではなくなったということです。
src/pkg/runtime/sys_freebsd_arm.s
の変更
このファイルは、FreeBSD/ARMアーキテクチャ固有のアセンブリ言語によるシステムコールラッパーを含んでいます。
runtime·kqueue(SB)
:kqueue()
システムコールを呼び出すためのラッパーが追加されています。SWI $362
は、FreeBSD/ARMにおけるkqueue
システムコールの番号です。runtime·kevent(SB)
:kevent()
システムコールを呼び出すためのラッパーが追加されています。SWI $363
は、FreeBSD/ARMにおけるkevent
システムコールの番号です。このラッパーは、kq
(kqueueディスクリプタ)、changelist
(変更リスト)、nchanges
(変更数)、eventlist
(イベントリスト)、nevents
(イベント数)、timeout
(タイムアウト)といった引数を適切にレジスタやスタックに配置してシステムコールを呼び出しています。特に、引数の一部をスタックに配置している点(ADD $4, R13
とSUB $4, R13
)は、ARMのABI(Application Binary Interface)に従った引数渡しを示しています。runtime·closeonexec(SB)
:fcntl()
システムコールを呼び出して、ファイルディスクリプタにFD_CLOEXEC
フラグを設定するためのラッパーが追加されています。SWI $92
はfcntl
システムコールの番号です。FD_CLOEXEC
フラグは、exec
系のシステムコールが実行された際に、そのファイルディスクリプタが自動的にクローズされるようにするもので、セキュリティやリソースリーク防止のために重要です。ネットワークポーラーがオープンするファイルディスクリプタに対してこのフラグを設定することで、子プロセスに不要なディスクリプタが継承されるのを防ぎます。
これらのアセンブリコードの追加は、GoランタイムがFreeBSD/ARM上で直接kqueue
システムコールを呼び出せるようにするための低レベルなインターフェースを提供します。
コアとなるコードの変更箇所
このコミットのコアとなる変更は、以下のファイルに集中しています。
src/pkg/runtime/defs_freebsd_arm.h
:kqueue
関連の定数とKevent
構造体の定義追加。src/pkg/runtime/sys_freebsd_arm.s
:kqueue()
、kevent()
、fcntl(FD_CLOEXEC)
システムコールを呼び出すためのアセンブリラッパーの追加。src/pkg/runtime/netpoll.goc
,src/pkg/runtime/netpoll_kqueue.c
,src/pkg/runtime/netpoll_stub.c
: ビルドタグの変更により、FreeBSD/ARMがkqueue
ベースのポーリングを使用するように設定。
コアとなるコードの解説
src/pkg/runtime/defs_freebsd_arm.h
enum {
+ EINTR = 0x4,
+ EFAULT = 0xe,
PROT_NONE = 0x0,
PROT_READ = 0x1,
PROT_WRITE = 0x2,
@@ -21,8 +24,6 @@ enum {
UMTX_OP_WAIT_UINT = 0xb,
UMTX_OP_WAKE = 0x3,
- EINTR = 0x4,
-
SIGHUP = 0x1,
SIGINT = 0x2,
SIGQUIT = 0x3,
@@ -74,6 +75,14 @@ enum {
ITIMER_REAL = 0x0,\n\tITIMER_VIRTUAL\t= 0x1,
ITIMER_PROF = 0x2,
+\n\tEV_ADD = 0x1,
+\tEV_DELETE = 0x2,
+\tEV_CLEAR = 0x20,
+\tEV_RECEIPT = 0x40,
+\tEV_ERROR = 0x4000,
+\tEVFILT_READ = -0x1,
+\tEVFILT_WRITE = -0x2,
};
typedef struct Rtprio Rtprio;
@@ -87,6 +96,7 @@ typedef struct Ucontext Ucontext;
typedef struct Timespec Timespec;
typedef struct Timeval Timeval;
typedef struct Itimerval Itimerval;
+typedef struct Kevent Kevent;
#pragma pack on
@@ -159,5 +169,14 @@ struct Itimerval {
Timeval it_value;
};
+struct Kevent {
+\tuint32 ident;
+\tint16 filter;
+\tuint16 flags;
+\tuint32 fflags;
+\tint32 data;
+\tbyte *udata;
+};
+
#pragma pack off
この部分では、kqueue
システムコールで使用される定数(EV_ADD
など)と、kevent
システムコールに渡されるKevent
構造体が定義されています。Kevent
構造体は、監視対象のイベントを記述するための重要なデータ構造であり、kqueue
ベースのポーリングを実装する上で不可欠です。
src/pkg/runtime/sys_freebsd_arm.s
// int32 runtime·kqueue(void)
TEXT runtime·kqueue(SB),NOSPLIT,$0
SWI $362 // sys_kqueue
RSB.CS $0, R0
RET
// int32 runtime·kevent(int kq, Kevent *changelist, int nchanges, Kevent *eventlist, int nevents, Timespec *timeout)
TEXT runtime·kevent(SB),NOSPLIT,$8
MOVW 0(FP), R0 // kq
MOVW 4(FP), R1 // changelist
MOVW 8(FP), R2 // nchanges
MOVW 12(FP), R3 // eventlist
MOVW 16(FP), R4 // nevents
MOVW R4, 4(R13)
MOVW 20(FP), R4 // timeout
MOVW R4, 8(R13)
ADD $4, R13 // pass arg 5 and 6 on stack
SWI $363 // sys_kevent
RSB.CS $0, R0
SUB $4, R13
RET
// void runtime·closeonexec(int32 fd)
TEXT runtime·closeonexec(SB),NOSPLIT,$0
MOVW 0(FP), R0 // fd
MOVW $2, R1 // F_SETFD
MOVW $1, R2 // FD_CLOEXEC
SWI $92 // sys_fcntl
RET
このアセンブリコードは、GoのランタイムがFreeBSD/ARM上で直接システムコールを呼び出すための低レベルなインターフェースを提供します。
runtime·kqueue
:kqueue
ディスクリプタを作成するためのsys_kqueue
システムコール(番号362)を呼び出します。runtime·kevent
:kevent
システムコール(番号363)を呼び出します。このシステムコールは、イベントの登録と発生したイベントの取得の両方に使用されます。引数はARMのABIに従ってレジスタ(R0-R3)とスタック(R13レジスタを基準としたオフセット)に配置されます。runtime·closeonexec
:fcntl
システムコール(番号92)を呼び出し、ファイルディスクリプタにFD_CLOEXEC
フラグを設定します。これは、exec
時にファイルディスクリプタがクローズされるようにするための重要な設定です。
これらのアセンブリラッパーは、GoのCgoを使わずに、Goランタイムが直接OSのシステムコールと対話できるようにするために必要です。これにより、GoのネットワークポーラーはFreeBSD/ARM上で効率的に動作することができます。
関連リンク
- Go言語公式ドキュメント: https://golang.org/doc/
- FreeBSD kqueue(2) マニュアルページ: https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
- FreeBSD kevent(2) マニュアルページ: https://www.freebsd.org/cgi/man.cgi?query=kevent&sektion=2
- Goのネットワークポーラーに関する一般的な解説(Goのバージョンによって実装は異なる可能性がありますが、概念は共通です):
- The Go netpoller: https://medium.com/@valyala/the-go-netpoller-100-times-faster-than-epoll-or-kqueue-1000000-connections-per-second-on-a-single-core-9f7c3e7e2e7 (これはより新しい記事ですが、概念理解に役立ちます)
参考にした情報源リンク
- Go言語のソースコード(特に
src/pkg/runtime
ディレクトリ) - FreeBSDのシステムコールに関するドキュメント
- ARMアーキテクチャのABIに関する情報
- 一般的なGoランタイムの内部動作に関する知識
kqueue
に関する技術記事やドキュメント- GitHubのコミット履歴と関連するGo issue/CL (Change List)