[インデックス 14753] ファイルの概要
このコミットは、Go言語の標準ライブラリnet
パッケージにおけるファイルディスクリプタ(FD)のポーリングに関するコードの整理を目的としています。具体的には、FreeBSD、NetBSD、OpenBSDという3つのBSD系OS向けにそれぞれ存在していたfd_freebsd.go
、fd_netbsd.go
、``fd_openbsd.goというほぼ同一の内容を持つファイルを、
fd_bsd.go`という単一のファイルに統合しています。これにより、コードの重複を排除し、保守性を向上させています。
コミット
commit 7c4183ac2439a48d42ae7491ef47a26dc469b5fd
Author: Dave Cheney <dave@cheney.net>
Date: Fri Dec 28 21:01:52 2012 +1100
net: consolidate fd_{free,net,open}bsd.go into fd_bsd.go
These files are identical, so probably pre date // +build.
With a little work, fd_darwin could be merged as well.
R=mikioh.mikioh, jsing, devon.odell, lucio.dere, minux.ma
CC=golang-dev
https://golang.org/cl/7004053
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/7c4183ac2439a48d42ae7491ef47a26dc469b5fd
元コミット内容
net: consolidate fd_{free,net,open}bsd.go into fd_bsd.go
These files are identical, so probably pre date // +build.
With a little work, fd_darwin could be merged as well.
R=mikioh.mikioh, jsing, devon.odell, lucio.dere, minux.ma
CC=golang-dev
https://golang.org/cl/7004053
変更の背景
この変更の主な背景は、Go言語のnet
パッケージ内に存在するBSD系OS(FreeBSD, NetBSD, OpenBSD)向けのファイルディスクリプタ(FD)ポーリング処理の実装が、それぞれfd_freebsd.go
、fd_netbsd.go
、fd_openbsd.go
という個別のファイルに分かれていたにもかかわらず、その内容がほぼ同一であったことです。
コミットメッセージにある「probably pre date // +build」という記述は、これらのファイルがGoのビルドタグ(// +build
ディレクティブ)が導入される以前、またはその利用が一般的になる以前に作成された可能性を示唆しています。ビルドタグは、特定のOSやアーキテクチャ向けにコードを条件付きでコンパイルするためのGoの機能であり、これを使用することで、複数のOSで共通のロジックを持つファイルを一つにまとめることが可能になります。
この重複は、コードの冗長性を生み出し、バグ修正や機能追加の際に複数のファイルを変更する必要があるため、保守性の低下を招いていました。したがって、これらの同一のファイルをfd_bsd.go
という単一のファイルに統合し、ビルドタグを用いて各OSに対応させることで、コードベースを簡潔にし、将来的なメンテナンスを容易にすることが変更の動機となっています。コミットメッセージには、将来的にはfd_darwin.go
(macOS向け)も同様に統合できる可能性が示唆されており、さらなるコードベースの整理への意欲が見て取れます。
前提知識の解説
1. ファイルディスクリプタ (File Descriptor, FD)
ファイルディスクリプタは、Unix系オペレーティングシステムにおいて、ファイルやソケット、パイプなどのI/Oリソースを識別するためにカーネルがプロセスに割り当てる非負の整数です。プログラムはFDを通じてこれらのリソースにアクセスし、読み書きなどの操作を行います。ネットワークプログラミングにおいては、ソケットがFDとして扱われ、データの送受信や接続の確立・切断が行われます。
2. kqueue/kevent
kqueue
とkevent
は、BSD系OS(FreeBSD, NetBSD, OpenBSD, macOSなど)で利用される、高性能なイベント通知インターフェースです。これは、ファイルディスクリプタの状態変化(読み込み可能、書き込み可能、エラー発生など)を効率的に監視するための仕組みです。
- kqueue: イベントキューを作成するためのシステムコールです。これにより、アプリケーションは監視したいイベントのリストをカーネルに登録し、イベントが発生するまで待機することができます。
- kevent:
kqueue
にイベントを登録したり、発生したイベントを取得したりするためのシステムコールです。アプリケーションはkevent
を使って、監視対象のFDとイベントの種類(例:EVFILT_READ
で読み込みイベント、EVFILT_WRITE
で書き込みイベント)を指定します。イベントが発生すると、kevent
は発生したイベントの詳細を返します。
kqueue
/kevent
は、多数のFDを同時に効率的に監視できるため、高並行なネットワークサーバーなどのアプリケーションで広く利用されています。Linuxにおけるepoll
やWindowsにおけるI/O Completion Ports (IOCP) と同種の機能を提供します。
3. Goのビルドタグ (// +build
)
Go言語には、ソースコードの特定のセクションを特定の環境でのみコンパイルするための「ビルドタグ」という機能があります。これは、Goソースファイルの先頭に// +build tag_name
のような形式でコメントとして記述されます。
例:
// +build linux
:Linux環境でのみコンパイルされる。// +build freebsd netbsd openbsd
:FreeBSD、NetBSD、OpenBSDのいずれかの環境でコンパイルされる。// +build !windows
:Windows以外の環境でコンパイルされる。
この機能により、OS固有のシステムコールやライブラリを使用するコードを、プラットフォームごとに異なるファイルに分離することなく、単一のファイル内で管理することが可能になります。コンパイラはビルド時にこれらのタグを読み取り、現在のターゲット環境に合致するコードのみをコンパイル対象とします。
4. Goの net
パッケージ
Go言語の標準ライブラリであるnet
パッケージは、ネットワークI/Oプリミティブを提供します。TCP/IP、UDP、Unixドメインソケットなどのネットワークプロトコルを扱うための機能が含まれています。このパッケージは、OSに依存する低レベルのネットワーク操作(ソケットの作成、接続、データの送受信、FDのポーリングなど)を抽象化し、Goプログラムからクロスプラットフォームで利用できるようにしています。内部的には、各OSのシステムコール(Linuxのepoll
、BSD系のkqueue
、WindowsのIOCPなど)を適切に利用して、効率的なネットワークI/Oを実現しています。
技術的詳細
このコミットの技術的な核心は、Goのビルドタグを活用して、複数のBSD系OS向けのFDポーリングロジックを単一のファイルに統合した点にあります。
-
ファイルのリネームと削除:
src/pkg/net/fd_freebsd.go
がsrc/pkg/net/fd_bsd.go
にリネームされました。これは、FreeBSD固有のファイルから、より汎用的なBSD系OS向けのファイルへと役割が変更されたことを意味します。src/pkg/net/fd_netbsd.go
とsrc/pkg/net/fd_openbsd.go
は完全に削除されました。これは、これらのファイルの内容がリネームされたfd_bsd.go
に吸収されたためです。
-
ビルドタグの追加:
- 新しくリネームされた
src/pkg/net/fd_bsd.go
ファイルの先頭に、以下のビルドタグが追加されました。
このタグにより、// +build freebsd netbsd openbsd
fd_bsd.go
ファイルは、GoコンパイラがFreeBSD、NetBSD、OpenBSDのいずれかのターゲットOS向けにビルドを行う場合にのみコンパイル対象となります。これにより、以前は個別のファイルで提供されていた各OS向けのFDポーリングロジックが、この単一のファイルで共通して扱われるようになります。
- 新しくリネームされた
-
コードの同一性:
- コミットメッセージが示唆するように、削除された
fd_netbsd.go
とfd_openbsd.go
の内容は、リネーム前のfd_freebsd.go
とほぼ同一でした。この同一性があったからこそ、ビルドタグを導入するだけでコードの統合が可能となりました。これにより、同じロジックが3つのファイルに分散していた状態が解消され、コードの重複が大幅に削減されました。
- コミットメッセージが示唆するように、削除された
この変更により、Goのnet
パッケージは、BSD系OSにおけるFDポーリングの実装をより効率的かつ保守性の高い形で管理できるようになりました。特定のOSに依存する部分をビルドタグで切り替えることで、コードベース全体の複雑さを軽減し、将来的な機能拡張やバグ修正が容易になります。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更は、主に以下のファイル操作と、fd_bsd.go
へのビルドタグの追加です。
--- a/src/pkg/net/fd_freebsd.go
+++ b/src/pkg/net/fd_bsd.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build freebsd netbsd openbsd
+
// Waiting for FDs via kqueue/kevent.
package net
diff --git a/src/pkg/net/fd_netbsd.go b/src/pkg/net/fd_netbsd.go
deleted file mode 100644
index 35d84c30ef..0000000000
--- a/src/pkg/net/fd_netbsd.go
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Waiting for FDs via kqueue/kevent.
-
-package net
-
-import (
- "os"
- "syscall"
-)
-
-type pollster struct {
- kq int
- eventbuf [10]syscall.Kevent_t
- events []syscall.Kevent_t
-
- // An event buffer for AddFD/DelFD.
- // Must hold pollServer lock.
- kbuf [1]syscall.Kevent_t
-}
-
-func newpollster() (p *pollster, err error) {
- p = new(pollster)
- if p.kq, err = syscall.Kqueue(); err != nil {
- return nil, os.NewSyscallError("kqueue", err)
- }
- syscall.CloseOnExec(p.kq)
- p.events = p.eventbuf[0:0]
- return p, nil
-}
-
-func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, error) {
- // pollServer is locked.
-
- var kmode int
- if mode == 'r' {
- kmode = syscall.EVFILT_READ
- } else {
- kmode = syscall.EVFILT_WRITE
- }
- ev := &p.kbuf[0]
- // EV_ADD - add event to kqueue list
- // EV_ONESHOT - delete the event the first time it triggers
- flags := syscall.EV_ADD
- if !repeat {
- flags |= syscall.EV_ONESHOT
- }
- syscall.SetKevent(ev, fd, kmode, flags)
-
- n, err := syscall.Kevent(p.kq, p.kbuf[:], nil, nil)
- if err != nil {
- return false, os.NewSyscallError("kevent", err)
- }
- if n != 1 || (ev.Flags&syscall.EV_ERROR) == 0 || int(ev.Ident) != fd || int(ev.Filter) != kmode {
- return false, os.NewSyscallError("kqueue phase error", err)
- }
- if ev.Data != 0 {
- return false, syscall.Errno(int(ev.Data))
- }
- return false, nil
-}
-
-func (p *pollster) DelFD(fd int, mode int) {
- // pollServer is locked.
-
- var kmode int
- if mode == 'r' {
- kmode = syscall.EVFILT_READ
- } else {
- kmode = syscall.EVFILT_WRITE
- }
- ev := &p.kbuf[0]
- // EV_DELETE - delete event from kqueue list
- syscall.SetKevent(ev, fd, kmode, syscall.EV_DELETE)
- syscall.Kevent(p.kq, p.kbuf[:], nil, nil)
-}
-
-func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err error) {
- var t *syscall.Timespec
- for len(p.events) == 0 {
- if nsec > 0 {
- if t == nil {
- t = new(syscall.Timespec)
- }
- *t = syscall.NsecToTimespec(nsec)
- }
-
- s.Unlock()
- n, err := syscall.Kevent(p.kq, nil, p.eventbuf[:], t)
- s.Lock()
-
- if err != nil {
- if err == syscall.EINTR {
- continue
- }
- return -1, 0, os.NewSyscallError("kevent", err)
- }
- if n == 0 {
- return -1, 0, nil
- }
- p.events = p.eventbuf[:n]
- }
- ev := &p.events[0]
- p.events = p.events[1:]
- fd = int(ev.Ident)
- if ev.Filter == syscall.EVFILT_READ {
- mode = 'r'
- } else {
- mode = 'w'
- }
- return fd, mode, nil
-}
-
-func (p *pollster) Close() error { return os.NewSyscallError("close", syscall.Close(p.kq)) }
diff --git a/src/pkg/net/fd_openbsd.go b/src/pkg/net/fd_openbsd.go
deleted file mode 100644
index 35d84c30ef..0000000000
--- a/src/pkg/net/fd_openbsd.go
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.\n
-// Waiting for FDs via kqueue/kevent.\n
-\n
-package net\n
-\n
-import (\n
- "os"\n
- "syscall"\n
-)\n
-\n
-type pollster struct {\n
- kq int\n
- eventbuf [10]syscall.Kevent_t\n
- events []syscall.Kevent_t\n
-\n
- // An event buffer for AddFD/DelFD.\n
- // Must hold pollServer lock.\n
- kbuf [1]syscall.Kevent_t\n
-}\n
-\n
-func newpollster() (p *pollster, err error) {\n
- p = new(pollster)\n
- if p.kq, err = syscall.Kqueue(); err != nil {\n
- return nil, os.NewSyscallError("kqueue", err)\n
- }\n
- syscall.CloseOnExec(p.kq)\n
- p.events = p.eventbuf[0:0]\n
- return p, nil\n
-}\n
-\n
-func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, error) {\n
- // pollServer is locked.\n
-\n
- var kmode int\n
- if mode == 'r' {\n
- kmode = syscall.EVFILT_READ\n
- } else {\n
- kmode = syscall.EVFILT_WRITE\n
- }\n
- ev := &p.kbuf[0]\n
- // EV_ADD - add event to kqueue list\n
- // EV_ONESHOT - delete the event the first time it triggers\n
- flags := syscall.EV_ADD\n
- if !repeat {\n
- flags |= syscall.EV_ONESHOT\n
- }\n
- syscall.SetKevent(ev, fd, kmode, flags)\n
-\n
- n, err := syscall.Kevent(p.kq, p.kbuf[:], nil, nil)\n
- if err != nil {\n
- return false, os.NewSyscallError("kevent", err)\n
- }\n
- if n != 1 || (ev.Flags&syscall.EV_ERROR) == 0 || int(ev.Ident) != fd || int(ev.Filter) != kmode {\n
- return false, os.NewSyscallError("kqueue phase error", err)\n
- }\n
- if ev.Data != 0 {\n
- return false, syscall.Errno(int(ev.Data))\n
- }\n
- return false, nil\n
-}\n
-\n
-func (p *pollster) DelFD(fd int, mode int) {\n
- // pollServer is locked.\n
-\n
- var kmode int\n
- if mode == 'r' {\n
- kmode = syscall.EVFILT_READ\n
- } else {\n
- kmode = syscall.EVFILT_WRITE\n
- }\n
- ev := &p.kbuf[0]\n
- // EV_DELETE - delete event from kqueue list\n
- syscall.SetKevent(ev, fd, kmode, syscall.EV_DELETE)\n
- syscall.Kevent(p.kq, p.kbuf[:], nil, nil)\n
-}\n
-\n
-func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err error) {\n
- var t *syscall.Timespec\n
- for len(p.events) == 0 {\n
- if nsec > 0 {\n
- if t == nil {\n
- t = new(syscall.Timespec)\n
- }\n
- *t = syscall.NsecToTimespec(nsec)\n
- }\n
-\n
- s.Unlock()\n
- n, err := syscall.Kevent(p.kq, nil, p.eventbuf[:], t)\n
- s.Lock()\n
-\n
- if err != nil {\n
- if err == syscall.EINTR {\n
- continue\n
- }\n
- return -1, 0, os.NewSyscallError("kevent", err)\n
- }\n
- if n == 0 {\n
- return -1, 0, nil\n
- }\n
- p.events = p.eventbuf[:n]\n
- }\n
- ev := &p.events[0]\n
- p.events = p.events[1:]\n
- fd = int(ev.Ident)\n
- if ev.Filter == syscall.EVFILT_READ {\n
- mode = 'r'\n
- } else {\n
- mode = 'w'\n
- }\n
- return fd, mode, nil\n
-}\n
-\n
-func (p *pollster) Close() error { return os.NewSyscallError("close", syscall.Close(p.kq)) }\n
この差分は以下の変更を示しています。
-
src/pkg/net/fd_freebsd.go
のリネームと+build
タグの追加:src/pkg/net/fd_freebsd.go
がsrc/pkg/net/fd_bsd.go
に名前が変更されました。- 新しい
fd_bsd.go
の2行目と3行目の間に、// +build freebsd netbsd openbsd
という行が追加されています。これは、このファイルがFreeBSD、NetBSD、OpenBSDのいずれかのOS向けにビルドされる際にのみコンパイルされることを示します。
-
src/pkg/net/fd_netbsd.go
の削除:- このファイルは完全に削除されました。その内容は
fd_bsd.go
に統合されました。
- このファイルは完全に削除されました。その内容は
-
src/pkg/net/fd_openbsd.go
の削除:- このファイルも完全に削除されました。その内容は
fd_bsd.go
に統合されました。
- このファイルも完全に削除されました。その内容は
これらの変更により、Goのビルドシステムは、BSD系OS向けにビルドする際に、fd_bsd.go
という単一のファイルを使用するようになります。
コアとなるコードの解説
統合されたfd_bsd.go
(旧fd_freebsd.go
、fd_netbsd.go
、fd_openbsd.go
)に含まれるコアとなるコードは、net
パッケージがBSD系OS上で非同期I/Oイベントを処理するために使用するpollster
構造体とその関連メソッドです。この実装は、kqueue
/kevent
システムコールを基盤としています。
pollster
構造体
type pollster struct {
kq int
eventbuf [10]syscall.Kevent_t
events []syscall.Kevent_t
// An event buffer for AddFD/DelFD.
// Must hold pollServer lock.
kbuf [1]syscall.Kevent_t
}
kq
:kqueue()
システムコールによって作成されたkqueueインスタンスのファイルディスクリプタを保持します。これがイベント監視の中心となります。eventbuf
:kevent
からイベントを受け取るための一時的なバッファです。syscall.Kevent_t
型の配列で、最大10個のイベントを一度に受け取ることができます。events
:eventbuf
から読み取られた、処理待ちのイベントを保持するスライスです。kbuf
:AddFD
やDelFD
メソッドで単一のイベントをkevent
に渡すための一時的なバッファです。pollServer
のロックを保持している間に使用されます。
newpollster()
関数
func newpollster() (p *pollster, err error) {
p = new(pollster)
if p.kq, err = syscall.Kqueue(); err != nil {
return nil, os.NewSyscallError("kqueue", err)
}
syscall.CloseOnExec(p.kq)
p.events = p.eventbuf[0:0]
return p, nil
}
pollster
の新しいインスタンスを初期化します。
syscall.Kqueue()
を呼び出して新しいkqueueを作成し、そのFDをp.kq
に格納します。- エラーが発生した場合は、
os.NewSyscallError
でラップして返します。 syscall.CloseOnExec(p.kq)
を呼び出し、p.kq
がexec
システムコールで子プロセスに継承されないように設定します。これは、ファイルディスクリプタのリークを防ぐための一般的なプラクティスです。p.events
スライスを初期化し、eventbuf
の先頭を指すようにします。
(p *pollster) AddFD(fd int, mode int, repeat bool) (bool, error)
メソッド
func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, error) {
// pollServer is locked.
var kmode int
if mode == 'r' {
kmode = syscall.EVFILT_READ
} else {
kmode = syscall.EVFILT_WRITE
}
ev := &p.kbuf[0]
// EV_ADD - add event to kqueue list
// EV_ONESHOT - delete the event the first time it triggers
flags := syscall.EV_ADD
if !repeat {
flags |= syscall.EV_ONESHOT
}
syscall.SetKevent(ev, fd, kmode, flags)
n, err := syscall.Kevent(p.kq, p.kbuf[:], nil, nil)
if err != nil {
return false, os.NewSyscallError("kevent", err)
}
if n != 1 || (ev.Flags&syscall.EV_ERROR) == 0 || int(ev.Ident) != fd || int(ev.Filter) != kmode {
return false, os.NewSyscallError("kqueue phase error", err)
}
if ev.Data != 0 {
return false, syscall.Errno(int(ev.Data))
}
return false, nil
}
指定されたファイルディスクリプタfd
をkqueueに登録し、監視を開始します。
mode
('r'または'w')に基づいて、監視するイベントフィルター(syscall.EVFILT_READ
またはsyscall.EVFILT_WRITE
)を設定します。syscall.SetKevent
を使用して、kbuf
内のKevent_t
構造体にイベント情報を設定します。EV_ADD
: イベントをkqueueに追加します。EV_ONESHOT
:repeat
がfalse
の場合、イベントが一度トリガーされたら自動的に削除されるように設定します。
syscall.Kevent
を呼び出して、設定したイベントをkqueueに登録します。kevent
の戻り値とev.Flags
をチェックし、エラーがないか、期待通りのイベントが登録されたかを確認します。
(p *pollster) DelFD(fd int, mode int)
メソッド
func (p *pollster) DelFD(fd int, mode int) {
// pollServer is locked.
var kmode int
if mode == 'r' {
kmode = syscall.EVFILT_READ
} else {
kmode = syscall.EVFILT_WRITE
}
ev := &p.kbuf[0]
// EV_DELETE - delete event from kqueue list
syscall.SetKevent(ev, fd, kmode, syscall.EV_DELETE)
syscall.Kevent(p.kq, p.kbuf[:], nil, nil)
}
指定されたファイルディスクリプタfd
の監視をkqueueから削除します。
AddFD
と同様に、mode
に基づいてイベントフィルターを設定します。syscall.SetKevent
でEV_DELETE
フラグを設定し、イベントを削除するように指示します。syscall.Kevent
を呼び出して、イベントの削除を実行します。
(p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err error)
メソッド
func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err error) {
var t *syscall.Timespec
for len(p.events) == 0 {
if nsec > 0 {
if t == nil {
t = new(syscall.Timespec)
}
*t = syscall.NsecToTimespec(nsec)
}
s.Unlock()
n, err := syscall.Kevent(p.kq, nil, p.eventbuf[:], t)
s.Lock()
if err != nil {
if err == syscall.EINTR {
continue
}
return -1, 0, os.NewSyscallError("kevent", err)
}
if n == 0 {
return -1, 0, nil
}
p.events = p.eventbuf[:n]
}
ev := &p.events[0]
p.events = p.events[1:]
fd = int(ev.Ident)
if ev.Filter == syscall.EVFILT_READ {
mode = 'r'
} else {
mode = 'w'
}
return fd, mode, nil
}
kqueueからイベントが発生するのを待ち、発生したイベントのFDとモードを返します。
p.events
に処理待ちのイベントがない場合、ループに入ります。nsec
(タイムアウト時間)が設定されている場合、syscall.Timespec
構造体に変換します。s.Unlock()
とs.Lock()
は、pollServer
のロックを一時的に解放し、syscall.Kevent
の呼び出し中に他のゴルーチンがpollServer
にアクセスできるようにします。これは、kevent
がブロッキングコールであるため、デッドロックを避けるために重要です。syscall.Kevent
を呼び出して、kqueueからイベントを取得します。- 第2引数に
nil
を渡すことで、新しいイベントの登録は行わず、既存のイベントの取得のみを行います。 - 第3引数に
p.eventbuf[:]
を渡すことで、イベントをこのバッファに書き込ませます。 - 第4引数に
t
を渡すことで、タイムアウトを設定します。
- 第2引数に
kevent
からの戻り値をチェックし、エラー処理(EINTR
の場合は再試行)を行います。- イベントが取得された場合、
p.events
スライスを更新し、取得されたイベントを格納します。 p.events
から最初のイベントを取り出し、そのFDとモード(読み込みまたは書き込み)を返します。
(p *pollster) Close() error
メソッド
func (p *pollster) Close() error { return os.NewSyscallError("close", syscall.Close(p.kq)) }
kqueueのファイルディスクリプタを閉じます。これにより、kqueueに関連付けられたリソースが解放されます。
これらのメソッドは、Goのnet
パッケージがBSD系OS上で効率的な非同期ネットワークI/Oを実現するための基盤を提供しています。kqueue
/kevent
の仕組みをGoのインターフェースにマッピングし、ファイルディスクリプタの監視とイベント通知を抽象化しています。
関連リンク
- Go言語の
net
パッケージ: https://pkg.go.dev/net - Go言語のビルド制約 (Build Constraints): https://pkg.go.dev/cmd/go#hdr-Build_constraints
- kqueue(2) - FreeBSD System Calls Manual: https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
- kevent(2) - FreeBSD System Calls Manual: https://www.freebsd.org/cgi/man.cgi?query=kevent&sektion=2
参考にした情報源リンク
- Go言語の公式ドキュメント
- BSD系OSのmanページ (
kqueue(2)
,kevent(2)
) - Go言語のソースコード (
src/pkg/net/
) - Go言語のビルドタグに関する一般的な情報源 (例: Go言語のブログ記事やチュートリアル)
- ファイルディスクリプタ、kqueue/keventに関する一般的なOSプログラミングの知識