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

[インデックス 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パッケージは、オペレーティングシステムの低レベルなシステムコールへのインターフェースを提供します。これは、より高レベルなパッケージが構築される基盤となる重要な層です。

技術的詳細

主な変更点

  1. 環境変数実装の移動: osパッケージからsyscallパッケージへ環境変数の実装を移動
  2. プラットフォーム固有ファイルの再構成: Unix、Windows、Plan 9の各プラットフォーム向けの実装をsyscallパッケージ内に配置
  3. APIの保持: osパッケージの公開APIは変更せず、内部実装のみをsyscallパッケージに委譲
  4. 不要な機能の削除: 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;

コアとなるコードの解説

環境変数の実装アーキテクチャ

新しい実装では、以下の階層構造を採用しています:

  1. ランタイム層: C言語で実装されたランタイムが、システムから環境変数を取得し、syscall·envsスライスに格納
  2. syscall層: Goのsyscallパッケージが、ランタイムから環境変数を取得し、マップ形式でキャッシュ
  3. os層: osパッケージが、syscallパッケージの機能を使用してユーザー向けのAPIを提供

同期制御

環境変数へのアクセスは、複数のゴルーチンから同時に行われる可能性があるため、適切な同期制御が必要です。新しい実装では、以下の同期メカニズムを使用しています:

  • envOnce: 環境変数の初期化を一度だけ実行するためのsync.Once
  • envLock: 環境変数マップへの読み書きを保護するためのsync.RWMutex

プラットフォーム対応

各プラットフォーム(Unix、Windows、Plan 9)に対して、それぞれ固有の実装を提供しています:

  • Unix系: POSIXの環境変数機能を使用
  • Windows: Win32 APIのGetEnvironmentVariableSetEnvironmentVariableを使用
  • Plan 9: /envファイルシステムを使用

関連リンク

参考にした情報源リンク