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

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

このコミットは、Go言語の標準ライブラリosパッケージから、プラットフォーム固有のSIGXXXシグナル変数を削除するものです。これにより、osパッケージの移植性が向上し、シグナルに関する定義がsyscallパッケージに一元化されます。

コミット

commit a3fdd6e64970b87768f3aed733c6fe72c983bb3e
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Fri Jan 27 14:47:02 2012 -0800

    os: remove SIGXXX signals variables.

    They're not portable, and pkg os is supposed to be portable.

    Fixes #2562

    R=golang-dev, mikioh.mikioh, r, n13m3y3r, rsc
    CC=golang-dev
    https://golang.org/cl/5574078

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

https://github.com/golang/go/commit/a3fdd6e64970b87768f3aed733c6fe72c983bb3e

元コミット内容

os: remove SIGXXX signals variables. They're not portable, and pkg os is supposed to be portable. Fixes #2562

変更の背景

Go言語のosパッケージは、オペレーティングシステムとの基本的な相互作用を提供することを目的としています。このパッケージは、ファイル操作、プロセス管理、環境変数へのアクセスなど、OSに依存する機能を提供しますが、その設計思想として「移植性 (portability)」が非常に重視されています。

このコミットが行われた2012年当時、osパッケージ内にはSIGXXX(例: SIGKILL, SIGHUPなど)といったシグナル定数が直接定義されていました。これらの定数は、各オペレーティングシステム(Linux, macOS, FreeBSD, Windowsなど)によって値が異なる場合があり、また、一部のシグナルは特定のOSにしか存在しないなど、その定義自体がプラットフォームに依存していました。

このような状況は、osパッケージの移植性という設計目標と矛盾していました。osパッケージを利用するアプリケーションが、特定のOSにしか存在しないシグナル定数に依存してしまうと、そのアプリケーションは他のOSで動作しなくなる可能性がありました。

この問題を解決するため、シグナルに関するプラットフォーム固有の定義をosパッケージから削除し、より低レベルでOS固有の機能を提供するsyscallパッケージに一元化するという方針が採られました。これにより、osパッケージはシグナルを抽象的なos.Signalインターフェースとして扱い、具体的なシグナル値はsyscallパッケージから取得するように変更されました。この変更は、Go言語の設計原則である「関心の分離 (separation of concerns)」にも合致しています。

コミットメッセージにある Fixes #2562 は、当時のGo言語のIssueトラッカー(おそらくGoogle Code上のもの)で報告されていた問題に対応するものです。このIssueは、osパッケージにおけるシグナル定数の移植性に関する課題を指摘していたと考えられます。

前提知識の解説

シグナル (Signal)

オペレーティングシステムにおけるシグナルは、プロセスに対して非同期的にイベントを通知するメカニズムです。例えば、ユーザーがCtrl+Cを押すとSIGINTシグナルが、プロセスが不正なメモリアクセスを試みるとSIGSEGVシグナルが、プロセスが終了するとSIGCHLDシグナルが送信されます。プロセスはこれらのシグナルを受信し、それぞれに対応する処理(シグナルハンドラ)を実行することができます。

シグナルには、以下のような種類があります。

  • 標準シグナル: POSIX標準で定義されているシグナルで、多くのUnix系OSで共通して利用されます(例: SIGINT, SIGTERM, SIGKILL, SIGHUPなど)。
  • リアルタイムシグナル: POSIX.1bで導入されたシグナルで、より柔軟な利用が可能です。
  • OS固有のシグナル: 特定のOSにのみ存在するシグナルです。

Go言語のosパッケージとsyscallパッケージ

  • osパッケージ: Go言語の標準ライブラリの一部で、オペレーティングシステムとの基本的な相互作用を提供します。ファイルシステム、プロセス、環境変数、シグナルなどの抽象化されたインターフェースを提供し、可能な限りプラットフォーム非依存なAPIを目指しています。
  • syscallパッケージ: Go言語の標準ライブラリの一部で、低レベルなシステムコールへのアクセスを提供します。このパッケージは、OS固有の定数、構造体、関数などを直接提供するため、非常にプラットフォーム依存性が高いです。通常、アプリケーション開発者が直接利用することは少なく、osパッケージのような高レベルな抽象化されたパッケージの内部で利用されます。

移植性 (Portability)

ソフトウェアの移植性とは、ある環境で開発されたソフトウェアが、他の異なる環境でも変更なし、または最小限の変更で動作する能力を指します。Go言語は、その設計思想として高い移植性を重視しており、異なるOSやアーキテクチャで同じコードベースが動作するように設計されています。

ビルドスクリプトとMakefile

Go言語のプロジェクトでは、ビルドプロセスを自動化するためにシェルスクリプトやMakefileが利用されることがあります。これらのスクリプトは、ソースファイルのコンパイル順序、依存関係、生成されるバイナリのパスなどを定義します。

技術的詳細

このコミットの主要な変更点は、osパッケージが提供していたプラットフォーム固有のSIGXXXシグナル定数を削除し、それらの参照をsyscallパッケージの対応する定数に置き換えたことです。

具体的には、以下のファイルが削除されました。

  1. src/pkg/os/mksignals.sh: このシェルスクリプトは、各OSおよびアーキテクチャ(例: darwin_386, linux_amd64, windows_386など)に対応するzsignal_*.goファイルを生成するためのメインスクリプトでした。
  2. src/pkg/os/mkunixsignals.sh: mksignals.shから呼び出され、Unix系OS向けのzsignal_*.goファイルを生成するスクリプトでした。syscallパッケージのzerrors_*.goファイル(システムコールエラーコードやシグナル定数が定義されている)を読み込み、osパッケージ内でSIGXXX = UnixSignal(syscall.SIGXXX)のような定数定義を生成していました。
  3. src/pkg/os/zsignal_*.go: これらのファイルは、mksignals.shおよびmkunixsignals.shによって自動生成されていたファイルです。各OS/アーキテクチャの組み合わせごとに存在し、そのプラットフォームで利用可能なSIGXXXシグナル定数をosパッケージの名前空間に公開していました。例えば、src/pkg/os/zsignal_darwin_386.goにはSIGKILL = UnixSignal(syscall.SIGKILL)のような定義が含まれていました。これらのファイルが削除されたことで、osパッケージはもはやプラットフォーム固有のシグナル定数を直接持つことはなくなりました。

これらのファイルの削除に伴い、以下の変更が行われました。

  • ビルドスクリプトの修正: src/buildscript/*.sh配下にある各プラットフォーム向けのビルドスクリプト(例: darwin_386.sh, linux_amd64.sh, windows_386.shなど)から、zsignal_*.goファイルのコンパイル対象からの除外が行われました。これにより、これらのファイルがビルドプロセスに含まれなくなりました。
  • src/pkg/os/Makefileの修正: osパッケージのMakefileから、GOFILES_変数(各OSでコンパイルされるGoファイルのリスト)に含まれていたzsignal_$(GOOS)_$(GOARCH).goの記述が削除されました。これはビルドスクリプトの変更と連携し、zsignalファイルがビルド対象から完全に除外されることを保証します。
  • src/pkg/os/exec_posix.goの修正: Process.Kill()メソッド内で、SIGKILLシグナルを送信する際に、以前はos.SIGKILLを直接使用していましたが、これをos.UnixSignal(syscall.SIGKILL)に置き換えました。これは、osパッケージ内のSIGKILL定数が削除されたため、syscallパッケージから直接SIGKILLの値を取得し、それをos.UnixSignal型にキャストして利用するように変更されたことを意味します。
  • src/pkg/os/exec_windows.goの修正: Windows環境でのプロセスシグナル処理においても、同様にSIGKILLの参照がsyscall.SIGKILLに置き換えられました。switch sig.(UnixSignal) { case SIGKILL: のようなパターンマッチングから、if us, ok := sig.(UnixSignal); ok && us == syscall.SIGKILL { のような型アサーションと値の比較に変わっています。これは、os.SIGKILL定数がなくなったため、UnixSignal型にキャスト可能かを確認し、その値がsyscall.SIGKILLと等しいかをチェックする形に変更されたものです。
  • src/pkg/os/signal/signal_test.goの修正: シグナルテストファイルでも、os.SIGHUPの直接参照が削除され、const sighup = os.UnixSignal(syscall.SIGHUP)という新しい定数が導入され、これを利用するように変更されました。これにより、テストコードも新しいシグナル参照のパターンに準拠するようになりました。

この変更により、osパッケージはシグナルに関するプラットフォーム固有の詳細を内部に持つことをやめ、syscallパッケージにその責任を委譲しました。osパッケージは、os.Signalという抽象的なインターフェースを通じてシグナルを扱い、具体的なシグナル値が必要な場合はsyscallパッケージから取得するという、よりクリーンで移植性の高い設計になりました。

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

このコミットのコアとなる変更は、主に以下の3つの側面から見ることができます。

  1. zsignal_*.go ファイル群の削除: 各OS/アーキテクチャ固有のシグナル定数を定義していた自動生成ファイルがすべて削除されました。

    • src/pkg/os/zsignal_darwin_386.go
    • src/pkg/os/zsignal_darwin_amd64.go
    • src/pkg/os/zsignal_freebsd_386.go
    • src/pkg/os/zsignal_freebsd_amd64.go
    • src/pkg/os/zsignal_linux_386.go
    • src/pkg/os/zsignal_linux_amd64.go
    • src/pkg/os/zsignal_linux_arm.go
    • src/pkg/os/zsignal_netbsd_386.go
    • src/pkg/os/zsignal_netbsd_amd64.go
    • src/pkg/os/zsignal_openbsd_386.go
    • src/pkg/os/zsignal_openbsd_amd64.go
    • src/pkg/os/zsignal_windows_386.go
    • src/pkg/os/zsignal_windows_amd64.go
  2. シグナル生成スクリプトの削除: 上記zsignal_*.goファイルを生成していたスクリプトが削除されました。

    • src/pkg/os/mksignals.sh
    • src/pkg/os/mkunixsignals.sh
  3. osパッケージ内のシグナル参照の変更: osパッケージ内で直接SIGXXX定数を使用していた箇所が、syscallパッケージの対応する定数に置き換えられました。

    • src/pkg/os/exec_posix.go:

      --- a/src/pkg/os/exec_posix.go
      +++ b/src/pkg/os/exec_posix.go
      @@ -48,7 +48,7 @@ func StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e
       
       // Kill causes the Process to exit immediately.
       func (p *Process) Kill() error {
      -	return p.Signal(SIGKILL)
      +	return p.Signal(UnixSignal(syscall.SIGKILL))
       }
       
       // Exec replaces the current process with an execution of the
      
    • src/pkg/os/exec_windows.go:

      --- a/src/pkg/os/exec_windows.go
      +++ b/src/pkg/os/exec_windows.go
      @@ -37,8 +37,7 @@ func (p *Process) Signal(sig Signal) error {
       	if p.done {
       		return errors.New("os: process already finished")
       	}
      -	switch sig.(UnixSignal) {
      -	case SIGKILL:
      +	if us, ok := sig.(UnixSignal); ok && us == syscall.SIGKILL {
       		e := syscall.TerminateProcess(syscall.Handle(p.handle), 1)
       		return NewSyscallError("TerminateProcess", e)
       	}
      
    • src/pkg/os/signal/signal_test.go:

      --- a/src/pkg/os/signal/signal_test.go
      +++ b/src/pkg/os/signal/signal_test.go
      @@ -12,11 +12,13 @@ import (
       	"testing"
       )
       
      +const sighup = os.UnixSignal(syscall.SIGHUP)
      +
       func TestSignal(t *testing.T) {
       	// Send this process a SIGHUP.
       	syscall.Syscall(syscall.SYS_KILL, uintptr(syscall.Getpid()), syscall.SIGHUP, 0)
       
      -	if sig := (<-Incoming).(os.UnixSignal); sig != os.SIGHUP {
      -		t.Errorf("signal was %v, want %v", sig, os.SIGHUP)
      +	if sig := (<-Incoming).(os.UnixSignal); sig != sighup {
      +		t.Errorf("signal was %v, want %v", sig, sighup)
       	}
       }
      

コアとなるコードの解説

このコミットの核心は、osパッケージがシグナルを扱う方法の根本的な変更にあります。

以前は、osパッケージは各プラットフォーム向けに自動生成されたzsignal_*.goファイルを通じて、os.SIGKILLos.SIGHUPといったシグナル定数を直接公開していました。これらの定数は、内部的にはsyscall.SIGKILLなどのsyscallパッケージの定数をos.UnixSignal型にキャストしたものでした。

しかし、このアプローチには以下の問題がありました。

  1. 移植性の欠如: zsignal_*.goファイルはプラットフォーム固有であり、osパッケージの設計目標である移植性と矛盾していました。特定のOSにしか存在しないシグナルがosパッケージのAPIとして公開される可能性がありました。
  2. 冗長性: syscallパッケージに既に存在するシグナル定数を、osパッケージで再度定義し直すことは冗長でした。
  3. ビルドプロセスの複雑化: mksignals.shmkunixsignals.shといった自動生成スクリプトが必要となり、ビルドプロセスが複雑になっていました。

このコミットでは、これらの問題を解決するために、zsignal_*.goファイルとそれらを生成するスクリプトを完全に削除しました。これにより、osパッケージはもはやSIGXXXという名前のシグナル定数を直接公開しなくなりました。

代わりに、osパッケージ内でシグナルを扱う必要がある箇所(例: Process.Kill()メソッド)では、直接syscallパッケージのシグナル定数(例: syscall.SIGKILL)を参照し、それをos.UnixSignal型にキャストして利用するように変更されました。

  • os.UnixSignal(syscall.SIGKILL):
    • syscall.SIGKILLは、syscallパッケージで定義されている、OS固有のSIGKILLシグナルの整数値です。これはプラットフォームによって異なる値を持つ可能性がありますが、syscallパッケージがその差異を吸収します。
    • os.UnixSignalは、osパッケージ内で定義されている型であり、syscallパッケージのシグナル値をosパッケージのシグナルとして扱うためのラッパーです。これにより、osパッケージはシグナルの具体的な数値表現に依存せず、抽象的なos.Signalインターフェースを通じてシグナルを処理できるようになります。

この変更により、osパッケージはシグナルの具体的な数値表現から完全に分離され、シグナルに関するプラットフォーム固有の詳細はsyscallパッケージに一元化されました。osパッケージは、より高レベルで抽象的なシグナル処理のインターフェースを提供することに専念できるようになり、Go言語の標準ライブラリ全体の移植性と保守性が向上しました。

関連リンク

参考にした情報源リンク