[インデックス 10813] ファイルの概要
このコミットは、Go言語のランタイムおよび標準ライブラリにNetBSDオペレーティングシステムへの対応を追加するものです。具体的には、以下のファイルが変更または新規作成されました。
include/libc.h
:RFMEM
マクロの定義にNetBSDを追加し、メモリ関連のフラグがNetBSDでも適切に扱われるように変更。src/libmach/netbsd.c
: NetBSD向けのlibmach
(低レベルのプロセス制御・デバッグライブラリ)のスタブ実装。現時点では未実装の関数がエラーを返すようになっています。src/pkg/net/fd_netbsd.go
: NetBSDにおけるファイルディスクリプタのI/O多重化(イベント通知)をkqueue
/kevent
システムコールを用いて実装。src/pkg/net/interface_netbsd.go
: NetBSD向けのネットワークインターフェース関連のスタブ実装。src/pkg/net/tcpsock_posix.go
: Goのビルドタグにnetbsd
を追加し、TCPソケットのPOSIX互換実装がNetBSDでもコンパイルされるように変更。src/pkg/os/stat_netbsd.go
: NetBSDにおけるファイル情報の取得(os.Stat
)をsyscall.Stat_t
構造体を用いて実装。
コミット
commit d10126a622d2e9010b5250d1bcec6acb51b4ce24
Author: Christopher Nielsen <m4dh4tt3r@gmail.com>
Date: Thu Dec 15 12:19:19 2011 -0500
os: OS-dependent bits to support NetBSD.
R=golang-dev, jsing
CC=golang-dev
https://golang.org/cl/5482068
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d10126a622d2e9010b5250d1bcec6acb51b4ce24
元コミット内容
commit d10126a622d2e9010b5250d1bcec6acb51b4ce24
Author: Christopher Nielsen <m4dh4tt3r@gmail.com>
Date: Thu Dec 15 12:19:19 2011 -0500
os: OS-dependent bits to support NetBSD.
R=golang-dev, jsing
CC=golang-dev
https://golang.org/cl/5482068
変更の背景
このコミットの主な背景は、Go言語のクロスプラットフォーム対応を強化し、NetBSDオペレーティングシステム上でのGoプログラムの実行を可能にすることです。Go言語は設計当初から高い移植性を目標としており、様々なOSやアーキテクチャで動作するように開発が進められてきました。
NetBSDは、その高い移植性とクリーンな設計で知られるUNIX系OSであり、多くの異なるハードウェアプラットフォームで動作します。Go言語がNetBSDをサポートすることで、Goで書かれたアプリケーションがNetBSD環境でも利用できるようになり、Goのエコシステムがさらに拡大します。
OSへの移植作業は、通常、そのOS固有のシステムコールやカーネルインターフェースをGoのランタイムや標準ライブラリから呼び出せるように実装することを伴います。特に、ファイルI/O、ネットワークI/O、プロセス管理、メモリ管理といった低レベルな機能はOS依存性が高いため、それぞれのOSに合わせて調整が必要です。このコミットは、NetBSDにおけるこれらの基本的なOS依存部分のサポートを追加する初期段階の作業と見られます。
前提知識の解説
NetBSD
NetBSDは、BSD系UNIXオペレーティングシステムの一つで、フリーかつオープンソースで開発されています。その最大の特徴は「Of course it runs NetBSD.」(もちろんNetBSDで動く)というスローガンが示すように、非常に高い移植性を持つことです。組み込みシステムから大規模サーバーまで、多種多様なハードウェアプラットフォームで動作します。Go言語がNetBSDをサポートすることは、Goアプリケーションの展開可能な範囲を広げる上で重要です。
Go言語のOS移植性
Go言語は、その設計思想として高いOS移植性を持っています。これは、Goの標準ライブラリがOSの抽象化レイヤーを提供し、異なるOSのシステムコールをGoのコードから透過的に呼び出せるようにしているためです。Goのソースコードには、+build
タグ(ビルドタグ)という特殊なコメントが使われることがあり、これにより特定のOSやアーキテクチャ向けにのみコンパイルされるファイルを指定できます。例えば、// +build netbsd
と書かれたファイルはNetBSD向けにビルドされる際にのみ含まれます。
kqueue
/kevent
kqueue
は、FreeBSD、OpenBSD、NetBSD、macOSなどのBSD系オペレーティングシステムで利用される、高性能なイベント通知インターフェースです。kevent
システムコールを通じて、ファイルディスクリプタ(ソケット、ファイルなど)に対するI/Oイベント(読み込み可能、書き込み可能など)や、プロセス状態の変化、タイマーイベントなどを効率的に監視できます。これはLinuxのepoll
や、より古いPOSIX標準のselect
/poll
に相当する機能で、多数のI/O操作を同時に扱うサーバーアプリケーションなどで特に重要です。Goのnet
パッケージでは、ネットワークI/Oの効率化のためにこのkqueue
が利用されます。
libmach
libmach
は、Goのランタイム内部で使用される低レベルのライブラリで、主にデバッガやプロファイラがプロセスのメモリやレジスタ、実行状態にアクセスするために使われます。OS固有のプロセス制御やメモリマッピングの機能を提供します。このコミットでは、NetBSD向けのlibmach
関数がスタブ(仮実装)として追加されており、現時点では未実装であることを示すエラーを返します。これは、基本的なOSサポートを優先し、より複雑なデバッグ機能は後回しにされたことを示唆しています。
Goのsyscall
、os
、net
パッケージ
syscall
パッケージ: GoプログラムからOSのシステムコールを直接呼び出すための低レベルなインターフェースを提供します。OS固有の定数や構造体も定義されており、OS移植の際にはこのパッケージのOS固有実装が重要になります。os
パッケージ: ファイルシステム操作、プロセス管理、環境変数など、OSに依存するがより高レベルな機能を提供します。os.Stat
関数はファイルのメタデータ(サイズ、更新日時、パーミッションなど)を取得するために使われます。net
パッケージ: ネットワークI/O(TCP/UDPソケット、IPアドレス、ネットワークインターフェースなど)を扱うための機能を提供します。このパッケージもOSに依存する部分が多く、特にソケットの非同期I/O処理にはOS固有のイベント通知メカニズム(kqueue
など)が利用されます。
技術的詳細
このコミットは、NetBSDへのGo言語の移植において、特に以下の技術的側面をカバーしています。
-
I/O多重化のサポート(
src/pkg/net/fd_netbsd.go
): NetBSDにおける効率的なネットワークI/Oを実現するため、kqueue
/kevent
インターフェースが導入されています。pollster
構造体がkqueue
ディスクリプタを保持し、AddFD
、DelFD
、WaitFD
といったメソッドを通じてファイルディスクリプタのイベントを監視します。syscall.Kqueue()
:kqueue
インスタンスを作成します。syscall.SetKevent()
:kevent_t
構造体を設定し、監視したいイベント(読み込み、書き込みなど)とフラグ(EV_ADD
でイベントを追加、EV_ONESHOT
で一度トリガーされたら削除、EV_DELETE
でイベントを削除)を指定します。syscall.Kevent()
:kqueue
にイベントを登録したり、発生したイベントを取得したりするために使用されます。WaitFD
では、イベントが発生するまでブロックするか、指定されたタイムアウトまで待機します。 この実装により、Goのnet
パッケージはNetBSD上で多数の同時接続を効率的に処理できるようになります。
-
ファイルシステム情報の取得(
src/pkg/os/stat_netbsd.go
):os.Stat
関数がNetBSDのファイルシステムからファイルメタデータを取得できるように実装されています。syscall.Stat_t
: NetBSDのstat
システムコールが返すファイル情報を格納するC言語の構造体に対応するGoの構造体です。fileInfoFromStat
関数:syscall.Stat_t
からGoのos.FileInfo
インターフェースを満たすFileStat
構造体への変換を行います。ファイルのサイズ、更新日時、パーミッション、ファイルタイプ(ブロックデバイス、ディレクトリ、FIFO、シンボリックリンク、ソケットなど)が適切にマッピングされます。timespecToTime
関数:syscall.Timespec
構造体(秒とナノ秒で時間を表現)をGoのtime.Time
型に変換します。
-
低レベルプロセス制御のスタブ(
src/libmach/netbsd.c
):ctlproc
、proctextfile
、procstatus
、attachproc
、detachproc
、procthreadpids
といった関数がsysfatal
(致命的エラー)を呼び出すスタブとして実装されています。これは、これらの機能がNetBSDではまだ完全にサポートされていないか、Goの基本的な動作には必須ではないため、後回しにされたことを示します。これにより、GoランタイムのNetBSDへの初期移植を迅速に進めることが可能になります。 -
ビルドタグの更新(
src/pkg/net/tcpsock_posix.go
):+build
タグにnetbsd
が追加されたことで、tcpsock_posix.go
ファイルがNetBSD向けにコンパイルされるようになります。このファイルには、POSIX互換のTCPソケット操作に関するコードが含まれており、NetBSDがPOSIX標準に準拠しているため、既存のコードを再利用できます。
コアとなるコードの変更箇所
src/pkg/net/fd_netbsd.go
(新規ファイル)
// 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)
}
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, e := syscall.Kevent(p.kq, p.kbuf[:], nil, nil)
if e != nil {
return false, os.NewSyscallError("kevent", e)
}
if n != 1 || (ev.Flags&syscall.EV_ERROR) == 0 || int(ev.Ident) != fd || int(ev.Filter) != kmode {
return false, os.NewSyscallError("kqueue phase error", e)
}
if ev.Data != 0 {
return false, syscall.Errno(int(ev.Data))
}
return false, 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()
nn, e := syscall.Kevent(p.kq, nil, p.eventbuf[:], t)
s.Lock()
if e != nil {
if e == syscall.EINTR {
continue
}
return -1, 0, os.NewSyscallError("kevent", e)
}
if nn == 0 {
return -1, 0, nil
}
p.events = p.eventbuf[0:nn]
}
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
}
src/pkg/os/stat_netbsd.go
(新規ファイル)
package os
import (
"syscall"
"time"
)
func sameFile(fs1, fs2 *FileStat) bool {
sys1 := fs1.Sys.(*syscall.Stat_t)
sys2 := fs2.Sys.(*syscall.Stat_t)
return sys1.Dev == sys2.Dev && sys1.Ino == sys2.Ino
}
func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo {
fs := &FileStat{
name: basename(name),
size: int64(st.Size),
modTime: timespecToTime(st.Mtim),
Sys: st,
}
fs.mode = FileMode(st.Mode & 0777)
switch st.Mode & syscall.S_IFMT {
case syscall.S_IFBLK, syscall.S_IFCHR:
fs.mode |= ModeDevice
case syscall.S_IFDIR:
fs.mode |= ModeDir
case syscall.S_IFIFO:
fs.mode |= ModeNamedPipe
case syscall.S_IFLNK:
fs.mode |= ModeSymlink
case syscall.S_IFREG:
// nothing to do
case syscall.S_IFSOCK:
fs.mode |= ModeSocket
}
if st.Mode&syscall.S_ISGID != 0 {
fs.mode |= ModeSetgid
}
if st.Mode&syscall.S_ISUID != 0 {
fs.mode |= ModeSetuid
}
return fs
}
func timespecToTime(ts syscall.Timespec) time.Time {
return time.Unix(int64(ts.Sec), int64(ts.Nsec))
}
コアとなるコードの解説
src/pkg/net/fd_netbsd.go
このファイルは、NetBSDにおけるファイルディスクリプタのイベント監視メカニズムであるkqueue
/kevent
をGoのnet
パッケージに統合するものです。
pollster
構造体:kqueue
インスタンスのファイルディスクリプタ(kq
)と、kevent
システムコールから返されるイベントを格納するためのバッファ(eventbuf
,events
)、そしてイベント登録時に使用する一時バッファ(kbuf
)を保持します。newpollster()
:syscall.Kqueue()
を呼び出して新しいkqueue
インスタンスを作成し、pollster
構造体を初期化します。エラーが発生した場合はos.NewSyscallError
でラップして返します。AddFD(fd int, mode int, repeat bool)
: 指定されたファイルディスクリプタ(fd
)とモード(読み込み'r'
または書き込み'w'
)に対してイベントを登録します。kmode
はsyscall.EVFILT_READ
またはsyscall.EVFILT_WRITE
に設定されます。syscall.SetKevent()
は、kevent_t
構造体にイベントの識別子(fd
)、フィルター(kmode
)、およびフラグを設定します。flags
にはsyscall.EV_ADD
(イベントの追加)が常に含まれ、repeat
がfalse
の場合はsyscall.EV_ONESHOT
(イベントが一度トリガーされたら自動的に削除される)が追加されます。syscall.Kevent(p.kq, p.kbuf[:], nil, nil)
を呼び出すことで、kqueue
にイベントを登録します。
WaitFD(s *pollServer, nsec int64)
:kqueue
からイベントが発生するのを待ちます。len(p.events) == 0
の間、つまり内部バッファに処理すべきイベントがない間ループします。nsec
が正の値の場合、syscall.NsecToTimespec(nsec)
でタイムアウトを設定します。s.Unlock()
とs.Lock()
は、pollServer
のロックを一時的に解放・再取得することで、Kevent
呼び出し中に他のゴルーチンがブロックされないようにします。syscall.Kevent(p.kq, nil, p.eventbuf[:], t)
を呼び出して、kqueue
からイベントを取得します。nil
のchangelist
はイベントの変更を登録しないことを意味し、eventlist
にp.eventbuf
を指定してイベントを受け取ります。- イベントが取得されると、
p.events
スライスが更新され、最初のイベントが返されます。ev.Filter
がsyscall.EVFILT_READ
であれば読み込みモード、そうでなければ書き込みモードと判断されます。
src/pkg/os/stat_netbsd.go
このファイルは、NetBSDにおけるファイル情報の取得(os.Stat
)の実装を提供します。
sameFile(fs1, fs2 *FileStat)
: 2つのFileStat
が同じファイルを参照しているかどうかを判断します。これは、syscall.Stat_t
構造体のDev
(デバイスID)とIno
(inode番号)を比較することで行われます。UNIX系システムでは、デバイスIDとinode番号の組み合わせが一意にファイルを識別します。fileInfoFromStat(st *syscall.Stat_t, name string)
: NetBSDのstat
システムコールから得られたsyscall.Stat_t
構造体を、Goのos.FileInfo
インターフェースを満たすFileStat
構造体に変換します。name
、size
、modTime
(最終更新時刻)がst
から設定されます。modTime
はtimespecToTime
関数で変換されます。fs.mode
は、st.Mode
(ファイルモード)からパーミッションビット(0777
)を抽出し、さらにsyscall.S_IFMT
(ファイルタイプマスク)を使ってファイルタイプ(ブロックデバイス、ディレクトリ、FIFO、シンボリックリンク、通常ファイル、ソケット)を判別し、対応するos.FileMode
フラグを設定します。syscall.S_ISGID
とsyscall.S_ISUID
ビットをチェックし、SetGID/SetUIDビットが設定されているかどうかに応じてModeSetgid
/ModeSetuid
フラグを設定します。
timespecToTime(ts syscall.Timespec)
:syscall.Timespec
構造体(秒とナノ秒)をGoのtime.Time
型に変換するヘルパー関数です。time.Unix
関数を使用して、Unixエポックからの秒数とナノ秒数からtime.Time
オブジェクトを作成します。
これらの変更により、GoプログラムはNetBSD上でファイルシステムとネットワークI/Oを適切に処理できるようになります。
関連リンク
- Go CL 5482068: https://golang.org/cl/5482068
参考にした情報源リンク
- NetBSD 公式サイト: https://www.netbsd.org/
- kqueue(2) - NetBSD System Calls Manual: https://man.netbsd.org/kqueue.2
- kevent(2) - NetBSD System Calls Manual: https://man.netbsd.org/kevent.2
- stat(2) - NetBSD System Calls Manual: https://man.netbsd.org/stat.2
- Go言語のビルド制約 (Build Constraints): https://pkg.go.dev/go/build#hdr-Build_Constraints
- Go言語の
os
パッケージドキュメント: https://pkg.go.dev/os - Go言語の
net
パッケージドキュメント: https://pkg.go.dev/net - Go言語の
syscall
パッケージドキュメント: https://pkg.go.dev/syscall