[インデックス 10382] ファイルの概要
コミット
コミットハッシュ: 0acd879c267061814810f70d0f13b4c8767268b8 作成者: Russ Cox rsc@golang.org 日付: 2011年11月14日 14:06:50 -0500 タイトル: syscall: take over env implementation
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/0acd879c267061814810f70d0f13b4c8767268b8
元コミット内容
syscall: take over env implementation
The environment is needed by package time, which
we want not to depend on os (so that os can use
time.Time), so push down into syscall.
Delete syscall.Sleep, now unnecessary.
The package os environment API is preserved;
it is only the implementation that is moving to syscall.
Delete os.Envs, which was undocumented,
uninitialized on Windows and Plan 9, and
not maintained by Setenv and Clearenv.
Code can call os.Environ instead.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5370091
変更の背景
このコミットは、Go 1.0のリリースに向けた重要な準備作業の一環として行われました。2011年11月の時点で、Goチームは言語の安定性を向上させるため、パッケージ間の依存関係を整理する必要がありました。
最も重要な動機は、package time
が環境変数機能を必要としているにも関わらず、os
パッケージに依存することによって循環依存を引き起こしていたことです。os
パッケージはtime.Time
型を使用したいため、time
パッケージがos
パッケージに依存することは論理的に不可能でした。
この問題を解決するため、環境変数の実装をsyscall
パッケージに移動することで、より低レベルなレイヤーに機能を配置し、依存関係の整理を行いました。これにより、time
パッケージはsyscall
パッケージに依存し、os
パッケージはtime
パッケージに依存するという、より自然な依存関係の階層が実現されました。
前提知識の解説
Goのパッケージ依存関係
Goでは、パッケージ間の依存関係は非循環的でなければなりません。つまり、パッケージAがパッケージBに依存している場合、パッケージBはパッケージAに依存することはできません。これは、コンパイル時の依存関係グラフを単純化し、コンパイル速度を向上させるための重要な設計決定です。
環境変数の重要性
環境変数は、プログラムが実行時に外部から設定情報を取得するための標準的な方法です。Goでは、os.Getenv()
、os.Setenv()
、os.Clearenv()
、os.Environ()
といった関数を通じて環境変数にアクセスできます。
syscallパッケージの役割
syscall
パッケージは、オペレーティングシステムの低レベルなシステムコールへのインターフェースを提供します。これは、より高レベルなパッケージが構築される基盤となる重要な層です。
技術的詳細
主な変更点
- 環境変数実装の移動:
os
パッケージからsyscall
パッケージへ環境変数の実装を移動 - プラットフォーム固有ファイルの再構成: Unix、Windows、Plan 9の各プラットフォーム向けの実装を
syscall
パッケージ内に配置 - APIの保持:
os
パッケージの公開APIは変更せず、内部実装のみをsyscall
パッケージに委譲 - 不要な機能の削除:
syscall.Sleep
関数とos.Envs
変数の削除
変更されたファイル
このコミットでは、23個のファイルが変更され、348行が追加、386行が削除されました。
削除されたファイル:
src/pkg/os/env_plan9.go
src/pkg/os/env_unix.go
src/pkg/os/env_windows.go
新しく追加されたファイル:
src/pkg/syscall/env_plan9.go
src/pkg/syscall/env_unix.go
src/pkg/syscall/env_windows.go
コアとなるコードの変更箇所
1. os/env.go の変更
// 変更前
func setenv_c(k, v string)
// 変更後
import (
"errors"
"syscall"
)
func Getenv(key string) string {
v, _ := Getenverror(key)
return v
}
func Setenv(key, value string) error {
err := syscall.Setenv(key, value)
if err != nil {
return NewSyscallError("setenv", err)
}
return nil
}
2. syscall/env_unix.go の新規作成
package syscall
import "sync"
var env map[string]string
var envOnce sync.Once
var envs []string // provided by runtime
func Getenv(key string) (value string, found bool) {
envOnce.Do(copyenv)
if len(key) == 0 {
return "", false
}
envLock.RLock()
defer envLock.RUnlock()
v, ok := env[key]
if !ok {
return "", false
}
return v, true
}
3. ランタイムの変更
// runtime/runtime.c
// 変更前
Slice os·Envs;
// 変更後
Slice syscall·envs;
コアとなるコードの解説
環境変数の実装アーキテクチャ
新しい実装では、以下の階層構造を採用しています:
- ランタイム層: C言語で実装されたランタイムが、システムから環境変数を取得し、
syscall·envs
スライスに格納 - syscall層: Goの
syscall
パッケージが、ランタイムから環境変数を取得し、マップ形式でキャッシュ - os層:
os
パッケージが、syscall
パッケージの機能を使用してユーザー向けのAPIを提供
同期制御
環境変数へのアクセスは、複数のゴルーチンから同時に行われる可能性があるため、適切な同期制御が必要です。新しい実装では、以下の同期メカニズムを使用しています:
envOnce
: 環境変数の初期化を一度だけ実行するためのsync.Once
envLock
: 環境変数マップへの読み書きを保護するためのsync.RWMutex
プラットフォーム対応
各プラットフォーム(Unix、Windows、Plan 9)に対して、それぞれ固有の実装を提供しています:
- Unix系: POSIXの環境変数機能を使用
- Windows: Win32 APIの
GetEnvironmentVariable
、SetEnvironmentVariable
を使用 - Plan 9:
/env
ファイルシステムを使用
関連リンク
- Go 1.0 Release Notes
- syscall package documentation
- os package documentation
- Go package design philosophy