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

[インデックス 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のsyscallosnetパッケージ

  • syscallパッケージ: GoプログラムからOSのシステムコールを直接呼び出すための低レベルなインターフェースを提供します。OS固有の定数や構造体も定義されており、OS移植の際にはこのパッケージのOS固有実装が重要になります。
  • osパッケージ: ファイルシステム操作、プロセス管理、環境変数など、OSに依存するがより高レベルな機能を提供します。os.Stat関数はファイルのメタデータ(サイズ、更新日時、パーミッションなど)を取得するために使われます。
  • netパッケージ: ネットワークI/O(TCP/UDPソケット、IPアドレス、ネットワークインターフェースなど)を扱うための機能を提供します。このパッケージもOSに依存する部分が多く、特にソケットの非同期I/O処理にはOS固有のイベント通知メカニズム(kqueueなど)が利用されます。

技術的詳細

このコミットは、NetBSDへのGo言語の移植において、特に以下の技術的側面をカバーしています。

  1. I/O多重化のサポート(src/pkg/net/fd_netbsd.go: NetBSDにおける効率的なネットワークI/Oを実現するため、kqueue/keventインターフェースが導入されています。pollster構造体がkqueueディスクリプタを保持し、AddFDDelFDWaitFDといったメソッドを通じてファイルディスクリプタのイベントを監視します。

    • syscall.Kqueue(): kqueueインスタンスを作成します。
    • syscall.SetKevent(): kevent_t構造体を設定し、監視したいイベント(読み込み、書き込みなど)とフラグ(EV_ADDでイベントを追加、EV_ONESHOTで一度トリガーされたら削除、EV_DELETEでイベントを削除)を指定します。
    • syscall.Kevent(): kqueueにイベントを登録したり、発生したイベントを取得したりするために使用されます。WaitFDでは、イベントが発生するまでブロックするか、指定されたタイムアウトまで待機します。 この実装により、GoのnetパッケージはNetBSD上で多数の同時接続を効率的に処理できるようになります。
  2. ファイルシステム情報の取得(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型に変換します。
  3. 低レベルプロセス制御のスタブ(src/libmach/netbsd.c: ctlprocproctextfileprocstatusattachprocdetachprocprocthreadpidsといった関数がsysfatal(致命的エラー)を呼び出すスタブとして実装されています。これは、これらの機能がNetBSDではまだ完全にサポートされていないか、Goの基本的な動作には必須ではないため、後回しにされたことを示します。これにより、GoランタイムのNetBSDへの初期移植を迅速に進めることが可能になります。

  4. ビルドタグの更新(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')に対してイベントを登録します。
    • kmodesyscall.EVFILT_READまたはsyscall.EVFILT_WRITEに設定されます。
    • syscall.SetKevent()は、kevent_t構造体にイベントの識別子(fd)、フィルター(kmode)、およびフラグを設定します。
    • flagsにはsyscall.EV_ADD(イベントの追加)が常に含まれ、repeatfalseの場合は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からイベントを取得します。nilchangelistはイベントの変更を登録しないことを意味し、eventlistp.eventbufを指定してイベントを受け取ります。
    • イベントが取得されると、p.eventsスライスが更新され、最初のイベントが返されます。ev.Filtersyscall.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構造体に変換します。
    • namesizemodTime(最終更新時刻)がstから設定されます。modTimetimespecToTime関数で変換されます。
    • fs.modeは、st.Mode(ファイルモード)からパーミッションビット(0777)を抽出し、さらにsyscall.S_IFMT(ファイルタイプマスク)を使ってファイルタイプ(ブロックデバイス、ディレクトリ、FIFO、シンボリックリンク、通常ファイル、ソケット)を判別し、対応するos.FileModeフラグを設定します。
    • syscall.S_ISGIDsyscall.S_ISUIDビットをチェックし、SetGID/SetUIDビットが設定されているかどうかに応じてModeSetgid/ModeSetuidフラグを設定します。
  • timespecToTime(ts syscall.Timespec): syscall.Timespec構造体(秒とナノ秒)をGoのtime.Time型に変換するヘルパー関数です。time.Unix関数を使用して、Unixエポックからの秒数とナノ秒数からtime.Timeオブジェクトを作成します。

これらの変更により、GoプログラムはNetBSD上でファイルシステムとネットワークI/Oを適切に処理できるようになります。

関連リンク

参考にした情報源リンク