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

[インデックス 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環境では、おそらく汎用的なポーリングメカニズム(例えば、selectpollのような、より効率の悪いもの)が使用されていたか、あるいはネットワークポーリング機能自体が十分に最適化されていなかった可能性があります。このコミットは、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のみをスケジューリングすることができます。

具体的には、ネットワークポーラーは以下の役割を担います。

  1. イベント登録: ネットワークソケット(ファイルディスクリプタ)をOSのイベント通知メカニズムに登録し、読み書き可能になった際に通知を受け取るように設定します。
  2. イベント待機: OSのイベント通知メカニズムからのイベントを効率的に待機します。
  3. イベント処理: イベントが発生した際に、対応するgoroutineを「実行可能」状態にし、Goスケジューラに引き渡します。

kqueue

kqueueは、FreeBSD、macOS、NetBSD、OpenBSDなどのBSD系OSで利用される、高性能なイベント通知インターフェースです。これは、ファイルディスクリプタ(ソケット、ファイル、パイプなど)の状態変化、プロセスイベント、タイマーイベントなど、様々な種類のイベントを効率的に監視するために設計されています。

kqueueの基本的な概念は以下の通りです。

  • kqueueディスクリプタ: kqueue()システムコールによって作成される、イベントキューへの参照です。
  • kevent構造体: 監視したいイベントの種類(読み込み可能、書き込み可能など)、対象のファイルディスクリプタ、イベント発生時のフラグなどを定義する構造体です。
  • kevent()システムコール: kevent()システムコールは、以下の2つの主要な機能を提供します。
    1. イベントの登録/変更/削除: changelist引数を通じて、監視対象のイベントをkqueueに登録、変更、または削除します。
    2. イベントの取得: eventlist引数を通じて、発生したイベントを取得します。イベントがない場合は、指定されたタイムアウト期間待機します。

kqueueは、イベント駆動型プログラミングにおいて、selectpollといった古いメカニズムに比べて、よりスケーラブルで効率的な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アーキテクチャ固有の定数や構造体の定義を含んでいます。

  • EINTREFAULTのエラーコードが再配置され、EFAULTが新しく追加されています。これは、システムコールからのエラーハンドリングに関連する可能性があります。
  • kqueueシステムコールで使用されるイベントフラグ(EV_ADD, EV_DELETE, EV_CLEAR, EV_RECEIPT, EV_ERROR)とイベントフィルター(EVFILT_READ, EVFILT_WRITE)が追加されています。これらはkeventシステムコールに渡すkevent構造体のflagsfilterフィールドで使用されます。
  • Kevent構造体の定義が追加されています。この構造体はkeventシステムコールでイベントの登録や取得に使用されるもので、ident(ファイルディスクリプタなど)、filter(イベントの種類)、flags(イベントの動作)、fflagsdataudata(ユーザーデータ)などのフィールドを持ちます。この構造体の追加は、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,amd64freebsd,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, R13SUB $4, R13)は、ARMのABI(Application Binary Interface)に従った引数渡しを示しています。
  • runtime·closeonexec(SB): fcntl()システムコールを呼び出して、ファイルディスクリプタにFD_CLOEXECフラグを設定するためのラッパーが追加されています。SWI $92fcntlシステムコールの番号です。FD_CLOEXECフラグは、exec系のシステムコールが実行された際に、そのファイルディスクリプタが自動的にクローズされるようにするもので、セキュリティやリソースリーク防止のために重要です。ネットワークポーラーがオープンするファイルディスクリプタに対してこのフラグを設定することで、子プロセスに不要なディスクリプタが継承されるのを防ぎます。

これらのアセンブリコードの追加は、GoランタイムがFreeBSD/ARM上で直接kqueueシステムコールを呼び出せるようにするための低レベルなインターフェースを提供します。

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

このコミットのコアとなる変更は、以下のファイルに集中しています。

  1. src/pkg/runtime/defs_freebsd_arm.h: kqueue関連の定数とKevent構造体の定義追加。
  2. src/pkg/runtime/sys_freebsd_arm.s: kqueue()kevent()fcntl(FD_CLOEXEC)システムコールを呼び出すためのアセンブリラッパーの追加。
  3. 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言語のソースコード(特にsrc/pkg/runtimeディレクトリ)
  • FreeBSDのシステムコールに関するドキュメント
  • ARMアーキテクチャのABIに関する情報
  • 一般的なGoランタイムの内部動作に関する知識
  • kqueueに関する技術記事やドキュメント
  • GitHubのコミット履歴と関連するGo issue/CL (Change List)