[インデックス 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で表現したものです。
技術的詳細
このコミットの主な技術的変更点は以下の通りです。
-
ビルドタグへの
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向けのコードをビルドするための標準的なアプローチです。 -
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の標準的な時刻型で扱えるようにするために必要です。
-
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でファイルディスクリプタのリークが発生する可能性があり、その原因究明が今後の課題であることを示唆しています。
-
src/pkg/os/os_test.go
の変更:TestOpenError
関数内で、DragonFly BSDがディレクトリを書き込みモードで開こうとした際に、EISDIR
(ディレクトリである)ではなくEACCES
(アクセス拒否)を返すという挙動に対応するための特別な処理が追加されています。これは、OS間のエラーコードのセマンティクスの違いを吸収するためのものです。
これらの変更により、Goのos
パッケージはDragonFly BSDのシステムコールとファイルシステムAPIを正しく利用し、Goプログラムがこのプラットフォーム上で期待通りに動作するようになります。
コアとなるコードの変更箇所
このコミットのコアとなる変更は、主に以下のファイルとコードスニペットに集約されます。
-
ビルドタグの追加:
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
-
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) }
-
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
-
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
におけるEISDIR
とEACCES
の挙動の違いへの対応は、OS間の細かなセマンティクスの違いを吸収するための典型的な例です。Goのos
パッケージは、可能な限りOS間の差異を抽象化しようとしますが、低レベルのエラーコードの解釈が異なる場合、このようなプラットフォーム固有の調整が必要になります。これにより、Goプログラムは異なるOS上でも一貫したエラー処理ロジックを維持できます。
これらの変更は、GoがDragonFly BSD上で堅牢かつ効率的に動作するための基盤を築くものであり、Goのクロスプラットフォーム対応の哲学を体現しています。
関連リンク
- Go言語公式ウェブサイト: https://golang.org/
- DragonFly BSD公式ウェブサイト: https://www.dragonflybsd.org/
- Go
os
パッケージのドキュメント: https://pkg.go.dev/os - Go
syscall
パッケージのドキュメント: https://pkg.go.dev/syscall
参考にした情報源リンク
- 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の変更リスト)- この変更リストは、コミットに至るまでの議論や追加のコンテキストを提供します。
- 特に、ファイルディスクリプタリークに関する議論や、
EISDIR
とEACCES
の挙動に関する詳細な説明が含まれている可能性があります。 - Gerritのページは現在、GoのGitHubリポジトリにリダイレクトされるため、直接アクセスしてもGitHubのコミットページに到達します。