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

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

このコミットは、Go言語の標準ライブラリであるosパッケージに、DragonFly BSDオペレーティングシステムへの対応を追加するものです。これにより、GoプログラムがDragonFly BSD環境で適切にビルドされ、動作するようになります。

コミット

commit 6939061d47f807fbdb9c43859a61c30c6b015da8
Author: Joel Sing <jsing@google.com>
Date:   Sat Aug 24 02:15:50 2013 +1000

    os: dragonfly support
    
    Make the os package build and work on dragonfly.
    
    R=bradfitz
    CC=golang-dev
    https://golang.org/cl/13183044

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

https://github.com/golang/go/commit/6939061d47f807fbdb9c43859a61c30c6b015da8

元コミット内容

os: dragonfly support Make the os package build and work on dragonfly.

このコミットの目的は、GoのosパッケージがDragonFly BSD上でビルドされ、正しく機能するようにすることです。

変更の背景

Go言語はクロスプラットフォーム対応を重視しており、様々なオペレーティングシステム上で動作するように設計されています。このコミットが行われた2013年当時、Goは既にLinux、macOS (Darwin)、FreeBSD、NetBSD、OpenBSD、Windowsなどの主要なOSをサポートしていました。しかし、DragonFly BSDはまだ公式にサポートされていませんでした。

DragonFly BSDは、FreeBSD 4.8からフォークしたオープンソースのUnix系オペレーティングシステムであり、特にSMP (Symmetric Multi-Processing) 環境でのパフォーマンスとスケーラビリティに焦点を当てています。GoプログラムがこのOS上で動作できるようにすることは、Goのエコシステムを拡大し、より多くの開発者やシステム管理者にとってGoを魅力的な選択肢にする上で重要でした。

このコミットは、GoのosパッケージがDragonFly BSDのシステムコールやファイルシステム構造、その他のOS固有の挙動に適切に対応できるようにするためのものです。

前提知識の解説

1. Goのビルドタグ (+build)

Go言語では、ファイルの先頭に+buildディレクティブを記述することで、特定の環境でのみコンパイルされるコードを記述できます。これは、オペレーティングシステム、アーキテクチャ、またはカスタムタグに基づいてコードを条件付きで含めるために使用されます。

例: // +build linux darwin freebsd この行があるファイルは、Linux、macOS、FreeBSD環境でのみコンパイルされます。このコミットでは、既存の+build行にdragonflyを追加することで、DragonFly BSD環境でもこれらのファイルがコンパイルされるようにしています。

2. osパッケージ

Goの標準ライブラリのosパッケージは、オペレーティングシステムとのインタフェースを提供します。これには、ファイルシステム操作(ファイルの読み書き、ディレクトリの作成など)、プロセス管理(プロセスの起動、環境変数の取得など)、シグナル処理などが含まれます。OS固有の挙動を抽象化し、クロスプラットフォームで一貫したAPIを提供することが目的です。

3. syscallパッケージ

syscallパッケージは、低レベルのオペレーティングシステムプリミティブへのアクセスを提供します。これは、GoがOS固有のシステムコールを直接呼び出すために使用されます。osパッケージの多くの機能は、内部的にsyscallパッケージを利用して実装されています。

4. syscall.Stat_t構造体

Unix系システムでは、ファイルのメタデータ(サイズ、パーミッション、最終更新時刻など)はstatシステムコールによって取得され、その結果はstat構造体に格納されます。Goのsyscallパッケージでは、各OSに対応するStat_t構造体が定義されており、これがOSから返されるstat情報を保持します。このコミットでは、DragonFly BSD固有のsyscall.Stat_t構造体からGoのos.FileInfoを構築するロジックが追加されています。

5. os.FileInfoインタフェース

os.FileInfoは、ファイルに関する情報(名前、サイズ、モード、最終更新時刻、基になるシステム依存のデータ)を提供するインタフェースです。os.Stat関数などがこのインタフェースを実装した値を返します。

6. timespec構造体

Unix系システムでは、時刻情報は通常、秒とナノ秒のペアで構成されるtimespec構造体で表現されます。syscall.Timespecは、このtimespec構造体をGoで表現したものです。

技術的詳細

このコミットの主な技術的変更点は以下の通りです。

  1. ビルドタグへのdragonflyの追加: 多くのsrc/pkg/osおよびsrc/pkg/os/exec内のファイル(例: dir_unix.go, env_unix_test.go, error_unix.go, lp_unix.go, exec_posix.go, file_posix.go, file_unix.go, os_test.go, os_unix_test.go, path_unix.go, pipe_bsd.go, signal_test.go, signal_unix.go)の+buildディレクティブにdragonflyが追加されています。これにより、これらのファイルがDragonFly BSD環境でコンパイル対象となります。これは、Goのクロスコンパイル機能を利用して、特定のOS向けのコードをビルドするための標準的なアプローチです。

  2. src/pkg/os/stat_dragonfly.goの新規追加: このファイルは、DragonFly BSD固有のファイルシステム統計情報(stat)の処理を実装しています。

    • sameFile関数: 2つのfileStatが同じファイルを参照しているかどうかを判断します。これは、syscall.Stat_t構造体のDev(デバイスID)とIno(inode番号)を比較することで行われます。
    • fileInfoFromStat関数: syscall.Stat_t構造体からGoのos.FileInfoインタフェースを実装するfileStat構造体を生成します。この関数は、st.Modeフィールドからファイルの種類(ブロックデバイス、文字デバイス、ディレクトリ、FIFO、シンボリックリンク、通常ファイル、ソケット)とパーミッション、特殊なモード(Setgid, Setuid, Sticky)を抽出し、os.FileModeに変換します。
    • timespecToTime関数: syscall.Timespec構造体をGoのtime.Time型に変換します。これは、ファイルのタイムスタンプ(最終更新時刻など)をGoの標準的な時刻型で扱えるようにするために必要です。
  3. src/pkg/os/exec/exec_test.goの変更:

    • TestHelperProcess関数内で、オープンファイルをリストするコマンドとして、dragonflyの場合にfstatを使用するように変更されています。これは、DragonFly BSDでlsofの代わりにfstatが一般的に使用されるためと考えられます。
    • TestHelperProcess内のファイルディスクリプタリークチェックにおいて、dragonflyの場合にTODO(jsing): Determine why DragonFly is leaking file descriptors...というコメントが追加されています。これは、コミット時点ではDragonFly BSDでファイルディスクリプタのリークが発生する可能性があり、その原因究明が今後の課題であることを示唆しています。
  4. src/pkg/os/os_test.goの変更:

    • TestOpenError関数内で、DragonFly BSDがディレクトリを書き込みモードで開こうとした際に、EISDIR(ディレクトリである)ではなくEACCES(アクセス拒否)を返すという挙動に対応するための特別な処理が追加されています。これは、OS間のエラーコードのセマンティクスの違いを吸収するためのものです。

これらの変更により、GoのosパッケージはDragonFly BSDのシステムコールとファイルシステムAPIを正しく利用し、Goプログラムがこのプラットフォーム上で期待通りに動作するようになります。

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

このコミットのコアとなる変更は、主に以下のファイルとコードスニペットに集約されます。

  1. ビルドタグの追加: src/pkg/os/dir_unix.go (および他の多くのファイル):

    --- a/src/pkg/os/dir_unix.go
    +++ b/src/pkg/os/dir_unix.go
    @@ -2,7 +2,7 @@
     // Use of this source code is governed by a BSD-style
     // license that can be found in the LICENSE file.\n
    -// +build darwin freebsd linux netbsd openbsd
    +// +build darwin dragonfly freebsd linux netbsd openbsd
     
     package os
    
  2. src/pkg/os/stat_dragonfly.goの新規追加: このファイル全体が新規追加されており、DragonFly BSD固有のstat情報処理ロジックを含んでいます。

    // 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.
    
    package os
    
    import (
    	"syscall"
    	"time"
    )
    
    func sameFile(fs1, fs2 *fileStat) bool {
    	stat1 := fs1.sys.(*syscall.Stat_t)
    	stat2 := fs2.sys.(*syscall.Stat_t)
    	return stat1.Dev == stat2.Dev && stat1.Ino == stat2.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:
    		fs.mode |= ModeDevice
    	case syscall.S_IFCHR:
    		fs.mode |= ModeDevice | ModeCharDevice
    	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
    	}
    	if st.Mode&syscall.S_ISVTX != 0 {
    		fs.mode |= ModeSticky
    	}
    	return fs
    }
    
    func timespecToTime(ts syscall.Timespec) time.Time {
    	return time.Unix(int64(ts.Sec), int64(ts.Nsec))
    }
    
    // For testing.
    func atime(fi FileInfo) time.Time {
    	return timespecToTime(fi.Sys().(*syscall.Stat_t).Atim)
    }
    
  3. src/pkg/os/exec/exec_test.goにおけるruntime.GOOSの分岐追加:

    --- a/src/pkg/os/exec/exec_test.go
    +++ b/src/pkg/os/exec/exec_test.go
    @@ -445,7 +445,7 @@ func TestHelperProcess(*testing.T) {
     	// Determine which command to use to display open files.
     	ofcmd := "lsof"
     	switch runtime.GOOS {
    -	case "freebsd", "netbsd", "openbsd":
    +	case "dragonfly", "freebsd", "netbsd", "openbsd":
     		ofcmd = "fstat"
     	}
     
    @@ -514,6 +514,9 @@ func TestHelperProcess(*testing.T) {
     		\tos.Exit(1)
     		}
     		switch runtime.GOOS {
    +		case "dragonfly":
    +			// TODO(jsing): Determine why DragonFly is leaking
    +			// file descriptors...
     		case "darwin":
     			// TODO(bradfitz): broken? Sometimes.
     			// http://golang.org/issue/2603
    
  4. src/pkg/os/os_test.goにおけるDragonFly BSDのエラー挙動対応:

    --- a/src/pkg/os/os_test.go
    +++ b/src/pkg/os/os_test.go
    @@ -825,9 +825,16 @@ func TestOpenError(t *testing.T) {
     			\tif !strings.HasSuffix(syscallErrStr, expectedErrStr) {
     				\tt.Errorf("Open(%q, %d) = _, %q; want suffix %q", tt.path, tt.mode, syscallErrStr, expectedErrStr)
     			\t}\n
    -\t\t\t} else {
    -\t\t\t\tt.Errorf("Open(%q, %d) = _, %q; want %q", tt.path, tt.mode, perr.Err.Error(), tt.error.Error())\n
    +\t\t\t\tcontinue\n
    +\t\t\t}\n
    +\t\t\tif runtime.GOOS == "dragonfly" {\n
    +\t\t\t\t// DragonFly incorrectly returns EACCES rather\n
    +\t\t\t\t// EISDIR when a directory is opened for write.\n
    +\t\t\t\tif tt.error == syscall.EISDIR && perr.Err == syscall.EACCES {\n
    +\t\t\t\t\tcontinue\n
    +\t\t\t\t}\n
     \t\t\t}\n
    +\t\t\tt.Errorf("Open(%q, %d) = _, %q; want %q", tt.path, tt.mode, perr.Err.Error(), tt.error.Error())\n
     \t\t}\n
     \t}\n
     }\n
    

コアとなるコードの解説

ビルドタグの追加

これはGoのクロスプラットフォーム開発における基本的なパターンです。+build dragonflyを追加することで、GoコンパイラはDragonFly BSDをターゲットとするビルド時にこれらのファイルを自動的に含めるようになります。これにより、既存のUnix系OS向けの共通コードベースをDragonFly BSDでも再利用しつつ、必要に応じてOS固有の調整を行うことが可能になります。

src/pkg/os/stat_dragonfly.go

このファイルは、GoのosパッケージがDragonFly BSDのファイルシステムと適切に連携するための鍵となります。

  • sameFile: Unix系システムでは、ファイルはデバイスID (st_dev) とinode番号 (st_ino) の組み合わせによって一意に識別されます。この関数は、2つのファイルが同じ物理的なファイルを参照しているかを効率的にチェックするために、これらのIDを比較します。
  • fileInfoFromStat: この関数は、DragonFly BSDのstatシステムコールが返すsyscall.Stat_t構造体から、Goの抽象化されたファイル情報であるos.FileInfoを生成します。
    • st.Sizeからファイルサイズを取得します。
    • st.Mtim(最終更新時刻)をtimespecToTime関数でtime.Timeに変換します。
    • st.Modeフィールドは、ファイルのタイプ(通常ファイル、ディレクトリ、シンボリックリンクなど)とパーミッション情報を含んでいます。ビットマスク操作(& 0777& syscall.S_IFMTなど)を使用して、これらの情報を抽出し、Goのos.FileModeフラグ(ModeDevice, ModeCharDevice, ModeDir, ModeNamedPipe, ModeSymlink, ModeSocket, ModeSetgid, ModeSetuid, ModeSticky)にマッピングします。これにより、GoプログラムはOSに依存しない形でファイルの種類や属性を扱えるようになります。
  • timespecToTime: syscall.Timespecは、Unix系システムで時刻を秒とナノ秒で表現する構造体です。Goのtime.Unix関数は、これらの秒とナノ秒の値を引数として受け取り、Goのtime.Time型に変換します。これは、OSから取得した低レベルの時刻情報をGoの標準的な時刻処理機能で利用できるようにするために不可欠です。

src/pkg/os/exec/exec_test.goの変更

  • fstatの使用: DragonFly BSDでは、オープンファイル情報を取得するためにlsofよりもfstatコマンドがより一般的または適切であるため、テストコードがこれに対応するように変更されています。
  • ファイルディスクリプタリークのTODOコメント: これは、GoのテストスイートがDragonFly BSD上で実行された際に、何らかのファイルディスクリプタのリークが検出されたことを示しています。このコメントは、この問題がコミット時点では未解決であり、今後の調査が必要であることを開発者に伝えています。このようなTODOコメントは、オープンソースプロジェクトにおける継続的な改善プロセスの一部です。

src/pkg/os/os_test.goの変更

  • エラー挙動の調整: TestOpenErrorにおけるEISDIREACCESの挙動の違いへの対応は、OS間の細かなセマンティクスの違いを吸収するための典型的な例です。Goのosパッケージは、可能な限りOS間の差異を抽象化しようとしますが、低レベルのエラーコードの解釈が異なる場合、このようなプラットフォーム固有の調整が必要になります。これにより、Goプログラムは異なるOS上でも一貫したエラー処理ロジックを維持できます。

これらの変更は、GoがDragonFly BSD上で堅牢かつ効率的に動作するための基盤を築くものであり、Goのクロスプラットフォーム対応の哲学を体現しています。

関連リンク

参考にした情報源リンク

  • Go言語のソースコード (特にsrc/pkg/osディレクトリ)
  • Go言語のビルドタグに関するドキュメント (Go ModulesやGo Commandのドキュメント内)
  • Unix系システムのstatシステムコールに関する一般的な情報 (manページなど)
  • DragonFly BSDのドキュメントやコミュニティ情報 (特定のシステムコール挙動の確認のため)
  • GoのIssueトラッカー (関連するバグ報告や議論の確認のため)
    • http://golang.org/issue/2603 (コミット内のコメントで参照されているGoのIssue)
  • Goのコードレビューシステム (Gerrit) の変更リスト: https://golang.org/cl/13183044 (コミットメッセージで参照されているGoの変更リスト)
    • この変更リストは、コミットに至るまでの議論や追加のコンテキストを提供します。
    • 特に、ファイルディスクリプタリークに関する議論や、EISDIREACCESの挙動に関する詳細な説明が含まれている可能性があります。
    • Gerritのページは現在、GoのGitHubリポジトリにリダイレクトされるため、直接アクセスしてもGitHubのコミットページに到達します。