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

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

このコミットは、GoランタイムのネットワークポーラーにおけるkqueueEV_RECEIPTフラグのサポートを削除し、BSD系OS(FreeBSD, OpenBSD)でのランタイム統合型ネットワークポーラーのビルドを可能にする変更です。これにより、将来的にこれらのプラットフォームでのネットワークポーラーの統合に向けた準備が進められます。

コミット

commit 8f746af65d563ce442720aeb35a0f80efc62b7d6
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date:   Thu Jul 25 00:24:17 2013 +0900

    runtime: drop EV_RECEIPT support from network pollster on kqueue
    
    Currently Darwin and FreeBSD support and NetBSD and OpenBSD do not
    support EV_RECEIPT flag. We will drop use of EV_RECEIPT for now.
    
    Also enables to build runtime-integrated network pollster on
    freebsd/amd64,386 and openbsd/amd64,386. It just does build but never
    runs pollster stuff.
    
    This is in preparation for runtime-integrated network pollster for BSD
    variants.
    
    Update #5199
    
    R=dvyukov, minux.ma
    CC=golang-dev
    https://golang.org/cl/11759044

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

https://github.com/golang/go/commit/8f746af65d563ce442720aeb35a0f80efc62b7d6

元コミット内容

runtime: drop EV_RECEIPT support from network pollster on kqueue

Currently Darwin and FreeBSD support and NetBSD and OpenBSD do not
support EV_RECEIPT flag. We will drop use of EV_RECEIPT for now.

Also enables to build runtime-integrated network pollster on
freebsd/amd64,386 and openbsd/amd64,386. It just does build but never
runs pollster stuff.

This is in preparation for runtime-integrated network pollster for BSD
variants.

Update #5199

R=dvyukov, minux.ma
CC=golang-dev
https://golang.org/cl/11759044

変更の背景

この変更の主な背景は、Goランタイムのネットワークポーラーが、異なるBSD系OS間でのEV_RECEIPTフラグのサポート状況の不一致に対応するためです。具体的には、Darwin(macOS)とFreeBSDはEV_RECEIPTをサポートしている一方で、NetBSDとOpenBSDはサポートしていませんでした。この不一致が原因で、クロスプラットフォームでの一貫した動作を保証することが困難でした。

また、このコミットは、将来的にGoランタイムに統合されたネットワークポーラーをBSD系OS(特にFreeBSDとOpenBSDのamd64および386アーキテクチャ)で完全に機能させるための準備段階でもあります。これまでは、これらのプラットフォームではネットワークポーラーがビルドされても実行されない状態でしたが、この変更によりビルドが可能になり、将来的な統合への道が開かれました。

Goのネットワークポーラーは、非同期I/O操作を効率的に処理するために不可欠なコンポーネントであり、異なるOSのI/O多重化メカニズム(Linuxのepoll、macOS/BSDのkqueueなど)を抽象化しています。この変更は、Goのネットワークスタックの移植性と堅牢性を向上させることを目的としています。

前提知識の解説

1. Goランタイムとネットワークポーラー

Go言語は、独自のランタイム(実行環境)を持っています。このランタイムは、ガベージコレクション、スケジューラ(ゴルーチンの管理)、そしてネットワークI/Oなど、プログラムの実行に必要な多くの低レベルな機能を提供します。

ネットワークポーラー(Network Pollster)は、Goランタイムの重要なコンポーネントの一つで、ネットワーク接続からのイベント(データの読み込み準備完了、書き込み準備完了など)を効率的に監視し、対応するゴルーチンをスケジューラに通知する役割を担っています。これにより、Goの非同期ネットワークI/Oが実現され、多数の同時接続を効率的に処理できるようになります。

OSごとに異なるI/O多重化メカニズムが存在するため、Goランタイムはこれらの違いを吸収し、統一されたインターフェースを提供しています。

2. kqueue

kqueueは、FreeBSD、NetBSD、OpenBSD、macOS(Darwin)などのBSD系OSで利用される、高性能なイベント通知インターフェースです。これは、LinuxのepollやWindowsのI/O Completion Ports (IOCP) に相当するもので、ファイルディスクリプタ(ソケット、ファイル、パイプなど)の状態変化を効率的に監視するために使用されます。

kqueueは、keventシステムコールを通じてイベントを登録し、発生したイベントを取得します。イベントの種類には、読み込み可能(EVFILT_READ)、書き込み可能(EVFILT_WRITE)、ファイル変更(EVFILT_VNODE)などがあります。

3. EV_RECEIPTフラグ

EV_RECEIPTは、kqueuekeventシステムコールでイベントを登録する際に使用できるフラグの一つです。このフラグは、イベントの登録(EV_ADD)が成功したかどうか、またはエラーが発生したかどうかを即座に確認するために使用されます。

通常、keventでイベントを登録する際、登録操作自体は成功しても、そのイベントが実際に有効になるまでに時間がかかる場合があります。EV_RECEIPTを使用すると、kevent呼び出しが完了した時点で、登録されたイベントの状態(成功、エラーコードなど)を即座に結果として受け取ることができます。これにより、イベント登録の成功を同期的に確認し、エラーハンドリングを簡素化できる可能性があります。

しかし、このフラグは全てのBSD系OSでサポートされているわけではありません。コミットメッセージにあるように、DarwinとFreeBSDはサポートしていますが、NetBSDとOpenBSDはサポートしていませんでした。この互換性の問題が、今回の変更の直接的な原因となっています。

4. ビルドタグ(+build

Goのソースコードでは、ファイルの先頭に+buildディレクティブを記述することで、特定のOSやアーキテクチャ、またはその他の条件に基づいてファイルを条件付きでコンパイルするかどうかを制御できます。これは、クロスプラットフォーム開発において、OS固有のコードを分離するために非常に便利です。

例:

  • // +build linux:Linuxでのみコンパイルされる
  • // +build darwin freebsd:macOSまたはFreeBSDでコンパイルされる
  • // +build freebsd,amd64:FreeBSDのamd64アーキテクチャでのみコンパイルされる

このコミットでは、+buildタグが変更され、特定のBSD系OSおよびアーキテクチャでネットワークポーラー関連のファイルがビルドされるように調整されています。

技術的詳細

このコミットの技術的な核心は、kqueueインターフェースを使用するGoランタイムのネットワークポーラー実装において、EV_RECEIPTフラグの使用を停止することです。

src/pkg/runtime/netpoll_kqueue.cファイルは、kqueueベースのネットワークポーラーの主要な実装を含んでいます。このファイル内のruntime·netpollopen関数は、新しいファイルディスクリプタ(ソケットなど)をポーラーに登録する際に呼び出されます。

変更前は、この関数内でkeventシステムコールを呼び出す際に、EV_ADD|EV_RECEIPT|EV_CLEARというフラグの組み合わせを使用していました。

  • EV_ADD: イベントをkqueueに登録します。
  • EV_RECEIPT: イベント登録の成功/失敗を即座に結果として返します。
  • EV_CLEAR: イベントが処理された後に、kqueueから自動的にクリアされます。

EV_RECEIPTフラグを使用すると、kevent呼び出しの戻り値として、登録操作自体の結果(エラーコードなど)を受け取ることが期待されます。変更前のコードでは、この戻り値をチェックし、EV_ERRORフラグやev[0].dataev[1].data(エラーコードが含まれる可能性がある)を検証するロジックが含まれていました。

しかし、NetBSDやOpenBSDがEV_RECEIPTをサポートしていないため、このフラグを使用するとこれらのプラットフォームで問題が発生する可能性がありました。そこで、このコミットではEV_RECEIPTフラグを削除し、EV_ADD|EV_CLEARのみを使用するように変更しました。

EV_RECEIPTの削除に伴い、kevent呼び出しの後のエラーチェックロジックも削除されました。これは、EV_RECEIPTがない場合、keventがイベント登録の結果を即座に返すことを期待しないためです。代わりに、keventはイベントを登録するだけで、その後のイベント発生は通常のポーリングループで検出されることになります。

また、keventの呼び出し方も変更されています。 変更前: n = runtime·kevent(kq, ev, 2, ev, 2, nil); 変更後: n = runtime·kevent(kq, ev, 2, nil, 0, nil); これは、イベントを登録する際に、keventの出力バッファ(ev)と出力イベント数(2)を渡す必要がなくなったことを意味します。登録操作自体は、入力バッファ(ev)と入力イベント数(2)のみで行われます。これは、EV_RECEIPTが削除されたため、登録操作の結果を即座に受け取る必要がなくなったことと整合しています。

さらに、+buildタグの変更により、src/pkg/runtime/netpoll.gocsrc/pkg/runtime/netpoll_kqueue.cがFreeBSDのamd64/386およびOpenBSDでもビルドされるようになりました。これにより、これらのプラットフォームでもGoランタイムのネットワークポーラーが利用できるようになり、将来的な完全な統合への第一歩となります。同時に、src/pkg/runtime/netpoll_stub.cのビルドタグからFreeBSDのamd64/386およびOpenBSDが削除され、これらのプラットフォームがスタブではなく実際のkqueue実装を使用するようになったことを示しています。

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

src/pkg/runtime/netpoll.goc

--- a/src/pkg/runtime/netpoll.goc
+++ b/src/pkg/runtime/netpoll.goc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build darwin linux windows
+// +build darwin freebsd,amd64 freebsd,386 linux openbsd windows
 
 package net
 
  • +buildタグにfreebsd,amd64, freebsd,386, openbsdが追加され、これらのプラットフォームでもnetpollパッケージがビルドされるようになりました。

src/pkg/runtime/netpoll_kqueue.c

--- a/src/pkg/runtime/netpoll_kqueue.c
+++ b/src/pkg/runtime/netpoll_kqueue.c
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build darwin
+// +build darwin freebsd,amd64 freebsd,386 openbsd
 
 #include "runtime.h"
 #include "defs_GOOS_GOARCH.h"
@@ -37,23 +37,15 @@ runtime·netpollopen(uintptr fd, PollDesc *pd)
 	// when fd is closed.
 
 	ev[0].ident = (uint32)fd;
 	ev[0].filter = EVFILT_READ;
-	ev[0].flags = EV_ADD|EV_RECEIPT|EV_CLEAR;
+	ev[0].flags = EV_ADD|EV_CLEAR;
 	ev[0].fflags = 0;
 	ev[0].data = 0;
 	ev[0].udata = (byte*)pd;
 	ev[1] = ev[0];
 	ev[1].filter = EVFILT_WRITE;
-	n = runtime·kevent(kq, ev, 2, ev, 2, nil);
+	n = runtime·kevent(kq, ev, 2, nil, 0, nil);
 	if(n < 0)
 		return -n;
-	if(n != 2 ||
-		(ev[0].flags&EV_ERROR) == 0 || ev[0].ident != (uint32)fd || ev[0].filter != EVFILT_READ ||
-		(ev[1].flags&EV_ERROR) == 0 || ev[1].ident != (uint32)fd || ev[1].filter != EVFILT_WRITE)
-		return EFAULT;  // just to mark out from other errors
-	if(ev[0].data != 0)
-		return ev[0].data;
-	if(ev[1].data != 0)
-		return ev[1].data;
 	return 0;
 }
  • +buildタグにfreebsd,amd64, freebsd,386, openbsdが追加され、これらのプラットフォームでもkqueueベースのネットワークポーラー実装がビルドされるようになりました。
  • runtime·netpollopen関数内で、ev[0].flagsからEV_RECEIPTフラグが削除されました。
  • runtime·keventの呼び出しで、出力イベントバッファと出力イベント数を指定する引数(ev, 2)がnil, 0に変更されました。
  • EV_RECEIPTに関連する、kevent呼び出し後のエラーチェックロジックが削除されました。

src/pkg/runtime/netpoll_stub.c

--- a/src/pkg/runtime/netpoll_stub.c
+++ b/src/pkg/runtime/netpoll_stub.c
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build freebsd netbsd openbsd plan9
+// +build freebsd,arm netbsd plan9
 
 #include "runtime.h"
 
  • +buildタグからfreebsd(amd64/386を含む)とopenbsdが削除され、freebsd,armのみが残りました。これは、これらのプラットフォームがスタブではなく、実際のkqueue実装を使用するようになったことを意味します。

コアとなるコードの解説

このコミットの主要な変更は、src/pkg/runtime/netpoll_kqueue.c内のruntime·netpollopen関数に集中しています。

  1. EV_RECEIPTフラグの削除: 変更前: ev[0].flags = EV_ADD|EV_RECEIPT|EV_CLEAR; 変更後: ev[0].flags = EV_ADD|EV_CLEAR; この変更により、kqueueにイベントを登録する際に、EV_RECEIPTフラグが使用されなくなりました。前述の通り、NetBSDやOpenBSDがこのフラグをサポートしていないため、クロスプラットフォーム互換性を確保するために削除されました。これにより、kevent呼び出しが即座に登録結果を返すことを期待しなくなります。

  2. kevent呼び出しの変更: 変更前: n = runtime·kevent(kq, ev, 2, ev, 2, nil); 変更後: n = runtime·kevent(kq, ev, 2, nil, 0, nil); keventシステムコールは、イベントの登録(入力)と発生したイベントの取得(出力)の両方に使用できます。変更前は、入力イベント(ev, 2)と出力イベント(ev, 2)の両方を指定していました。これはEV_RECEIPTフラグを使用している場合に、登録操作の結果を即座に出力バッファで受け取るためのパターンです。 EV_RECEIPTが削除されたため、登録操作の結果を即座に受け取る必要がなくなりました。したがって、出力バッファと出力イベント数をnil, 0に設定し、登録操作のみを行うように変更されました。これにより、keventはイベントを登録するだけで、その後のイベント発生はポーリングループで処理されることになります。

  3. エラーチェックロジックの削除: EV_RECEIPTフラグの削除に伴い、kevent呼び出しの直後にあった、EV_ERRORフラグやev[0].data, ev[1].dataをチェックするロジックが削除されました。これらのチェックはEV_RECEIPTが返す情報に依存していたため、フラグがなくなったことで不要になりました。

これらの変更により、Goランタイムのネットワークポーラーは、EV_RECEIPTフラグのサポート状況に依存することなく、より広範なBSD系OSで動作するようになります。また、+buildタグの調整により、FreeBSD/amd64,386およびOpenBSDでもkqueueベースのポーラーがビルドされるようになり、将来的な完全な統合に向けた重要なステップとなります。

関連リンク

参考にした情報源リンク