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

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

このコミットは、Go言語のsyscallパッケージにおけるPlan 9オペレーティングシステム向けのシグナル処理に関する変更です。具体的には、既存のSignal型を廃止し、代わりにNoteという新しい型を導入しています。この変更により、エクスポートされていたos.Plan9Note型が削除され、Plan 9におけるシグナル(Goでは「ノート」と呼ばれる)の扱いがよりシンプルかつ統一されたものになります。

コミット

commit fe5005f721d3036dd3c6fa5c0e7b43c188342859
Author: Anthony Martin <ality@pbrane.org>
Date:   Fri May 4 03:44:41 2012 -0700

    syscall: remove the Signal type on Plan 9
    
    Instead use a new type, "Note", whose underlying
    type is just a string.  This change allows us to
    remove the exported os.Plan9Note type.
    
    R=bradfitz, seed, rsc
    CC=golang-dev
    https://golang.org/cl/6015046

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

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

元コミット内容

Plan 9向けのsyscallパッケージからSignal型を削除し、代わりに基底型が文字列である新しいNote型を使用する。この変更により、エクスポートされていたos.Plan9Note型を削除することが可能になる。

変更の背景

Go言語は、様々なオペレーティングシステム(OS)をサポートするように設計されています。その中には、Unix系OS(Linux, macOSなど)やWindowsだけでなく、ベル研究所で開発された分散OSであるPlan 9も含まれます。

Unix系OSでは、プロセス間通信やプロセス制御のために「シグナル(Signal)」というメカニズムが広く使われています。Goのosパッケージやsyscallパッケージは、これらのシグナルを抽象化して扱うためのインターフェースを提供しています。

しかし、Plan 9ではUnixのシグナルとは異なる「ノート(Note)」という概念が使われています。ノートは、プロセスに対して文字列メッセージを送信するメカニズムであり、Unixのシグナルよりも柔軟な情報伝達が可能です。Goの初期の実装では、Plan 9のノートをUnixのシグナルインターフェースに合わせるために、syscall.Signal型やos.Plan9Note型といった抽象化レイヤーが導入されていました。

このコミットの背景には、Plan 9のノートをより自然な形でGoのシステムコール層で扱うこと、そしてUnix系OSのシグナルとPlan 9のノートという異なる概念を無理にSignalという単一の型で表現することによる複雑さや不整合を解消する意図があったと考えられます。特に、os.Plan9NoteというPlan 9固有の型がosパッケージでエクスポートされていることが、APIの統一性を損ねていた可能性があります。

この変更は、Plan 9のノートが本質的に文字列ベースであるという特性を活かし、syscallパッケージ内でNote型を文字列のエイリアスとして定義することで、よりシンプルで直感的なAPI設計を目指したものです。これにより、osパッケージからPlan 9固有の型を削除し、クロスプラットフォームなos.Signalインターフェースの背後で、各OSのシグナル/ノートの実装詳細を適切に隠蔽できるようになります。

前提知識の解説

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

  • osパッケージ: GoプログラムがOSと対話するための基本的な機能を提供します。ファイル操作、プロセス管理、環境変数へのアクセスなどが含まれます。os.Signalインターフェースは、OSシグナルを抽象的に表現するためのものです。
  • syscallパッケージ: OS固有のシステムコールへの低レベルなインターフェースを提供します。このパッケージはOSごとに異なる実装を持ち、osパッケージなどの高レベルなパッケージが内部で利用します。

2. Unix系OSにおけるシグナル (Signal)

Unix系OSでは、シグナルはソフトウェア割り込みの一種で、プロセスに特定のイベントが発生したことを通知するために使用されます。例えば、SIGINT(Ctrl+Cによる割り込み)、SIGKILL(強制終了)、SIGTERM(終了要求)などがあります。シグナルは通常、整数値で識別されます。

3. Plan 9におけるノート (Note)

Plan 9では、Unixのシグナルに代わるメカニズムとして「ノート」が導入されています。ノートは、プロセスに対して文字列メッセージを送信する機能です。これにより、単なる数値のシグナルよりも詳細な情報をプロセスに伝えることができます。例えば、プロセスが終了する際に「kill」というノートを受け取ったり、デバッガがプロセスに「debug」というノートを送ったりすることが可能です。ノートは/proc/<pid>/noteというファイルに書き込むことで送信されます。

4. /procファイルシステム

Unix系OSやPlan 9には、/procという仮想ファイルシステムが存在します。これは、実行中のプロセスに関する情報やシステムの状態をファイルとして公開するものです。各プロセスは通常、/proc/<pid>/というディレクトリを持ち、その中にプロセスのメモリマップ、ファイルディスクリプタ、そしてPlan 9の場合はノートを送信するためのnoteファイルなどが含まれます。

5. Goのインターフェース

Goのインターフェースは、メソッドのシグネチャの集合を定義する型です。ある型がインターフェースのすべてのメソッドを実装していれば、その型はそのインターフェースを満たしていると見なされます。os.Signalインターフェースは、Signal()というメソッドを持つことで、任意の型がシグナルとして扱われることを可能にします。

技術的詳細

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

  1. syscall.Signal型の削除とsyscall.Note型の導入:

    • Plan 9向けのsyscallパッケージ(src/pkg/syscall/syscall_plan9.go)から、以前定義されていたtype Signal intが削除されました。
    • 代わりに、type Note stringという新しい型が導入されました。このNote型は、os.Signalインターフェース(Signal() {})とString()メソッドを実装しています。これにより、Plan 9のノートがGoのos.Signalインターフェースを満たしつつ、その実体が文字列であることを明確に表現できるようになりました。
  2. os.Plan9Note型の削除:

    • src/pkg/os/exec.goから、os.Plan9Note型が完全に削除されました。これは、syscall.Noteが直接os.Signalインターフェースを満たすようになったため、osパッケージ側でPlan 9固有のシグナル型をエクスポートする必要がなくなったためです。
  3. os.Interruptos.KillのPlan 9実装の変更:

    • src/pkg/os/exec_plan9.goにおいて、os.Interruptos.Killの具体的な値が、以前のsyscall.SIGINTsyscall.SIGKILL(これらも削除された)から、新しく定義されたsyscall.Note("interrupt")syscall.Note("kill")に変更されました。これにより、Plan 9のノートの概念が直接的に反映されるようになりました。
  4. プロセスへのノート送信ロジックの変更:

    • src/pkg/os/exec_plan9.go(*Process).signalメソッドにおいて、プロセスにノートを送信するロジックが変更されました。以前は/proc/$pid/noteファイルに直接書き込んでいましたが、新しいwriteProcFileヘルパー関数が導入され、これを通じてノートが書き込まれるようになりました。
    • Killシグナル(ノート)の扱いが特別視され、(*Process).Kill()メソッドが直接呼び出されるようになりました。これは、Plan 9においてkillノートは/proc/$pid/noteではなく、/proc/$pid/ctlファイルに「kill」という文字列を書き込むことで実現されるためです。この変更により、Plan 9のkillメカニズムがより正確に反映されています。
  5. zerrors_plan9_386.goからのシグナル定数削除:

    • src/pkg/syscall/zerrors_plan9_386.goから、SIGINTSIGKILLの定数が削除されました。これらは以前syscall.Signal型として定義されていましたが、Signal型自体が削除されたため、不要になりました。

これらの変更により、GoのPlan 9サポートにおけるシグナル(ノート)の扱いが、よりPlan 9のネイティブなメカニズムに即したものとなり、かつGoのAPI設計の統一性が向上しました。

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

src/pkg/os/exec.go

  • Signalインターフェースの定義は残るが、InterruptKillのグローバル変数の定義が削除された。これは、OS固有のファイル(exec_plan9.goexec_posix.go)で定義されるようになったため。
--- a/src/pkg/os/exec.go
+++ b/src/pkg/os/exec.go
@@ -54,14 +54,6 @@ type Signal interface {
 	Signal() // to distinguish from other Stringers
 }
 
-// The only signal values guaranteed to be present on all systems
-// are Interrupt (send the process an interrupt) and
-// Kill (force the process to exit).\n-var (\n-\tInterrupt Signal = syscall.SIGINT\n-\tKill      Signal = syscall.SIGKILL\n-)\n-\n // Getpid returns the process id of the caller.\n func Getpid() int { return syscall.Getpid() }\n 

src/pkg/os/exec_plan9.go

  • InterruptKillsyscall.Note型として再定義された。
  • Plan9Note型が削除された。
  • writeProcFileヘルパー関数が追加された。
  • signalメソッドとkillメソッドがwriteProcFileを使用するように変更された。
--- a/src/pkg/os/exec_plan9.go
+++ b/src/pkg/os/exec_plan9.go
@@ -11,6 +11,14 @@ import (
 	"time"
 )
 
+// The only signal values guaranteed to be present on all systems
+// are Interrupt (send the process an interrupt) and Kill (force
+// the process to exit).
+var (
+	Interrupt Signal = syscall.Note("interrupt")
+	Kill      Signal = syscall.Note("kill")
+)
+
 func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err error) {
 	sysattr := &syscall.ProcAttr{
 		Dir: attr.Dir,
@@ -30,35 +38,35 @@ func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e
 	return newProcess(pid, h), nil
 }
 
-// Plan9Note implements the Signal interface on Plan 9.
-type Plan9Note string
-
-func (note Plan9Note) String() string {
-	return string(note)
+func (p *Process) writeProcFile(file string, data string) error {
+	f, e := OpenFile("/proc/"+itoa(p.Pid)+"/"+file, O_WRONLY, 0)
+	if e != nil {
+		return e
+	}
+	defer f.Close()
+	_, e = f.Write([]byte(data))
+	return e
 }
 
 func (p *Process) signal(sig Signal) error {
 	if p.done {
 		return errors.New("os: process already finished")
 	}
-
-	f, e := OpenFile("/proc/"+itoa(p.Pid)+"/note", O_WRONLY, 0)
-	if e != nil {
+	if sig == Kill {
+		// Special-case the kill signal since it doesn't use /proc/$pid/note.
+		return p.Kill()
+	}
+	if e := p.writeProcFile("note", sig.String()); e != nil {
 		return NewSyscallError("signal", e)
 	}
-	defer f.Close()
-	_, e = f.Write([]byte(sig.String()))
-	return e
+	return nil
 }
 
 func (p *Process) kill() error {
-	f, e := OpenFile("/proc/"+itoa(p.Pid)+"/ctl", O_WRONLY, 0)
-	if e != nil {
+	if e := p.writeProcFile("ctl", "kill"); e != nil {
 		return NewSyscallError("kill", e)
 	}
-	defer f.Close()
-	_, e = f.Write([]byte("kill"))
-	return e
+	return nil
 }
 
 func (p *Process) wait() (ps *ProcessState, err error) {

src/pkg/os/exec_posix.go

  • InterruptKillsyscall.SIGINTsyscall.SIGKILLとして再定義された。これは、os/exec.goから移動されたもの。
--- a/src/pkg/os/exec_posix.go
+++ b/src/pkg/os/exec_posix.go
@@ -10,6 +10,14 @@ import (
 	"syscall"
 )
 
+// The only signal values guaranteed to be present on all systems
+// are Interrupt (send the process an interrupt) and Kill (force
+// the process to exit).
+var (
+	Interrupt Signal = syscall.SIGINT
+	Kill      Signal = syscall.SIGKILL
+)
+
 func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err error) {
 	// Double-check existence of the directory we want
 	// to chdir into.  We can make the error clearer this way.

src/pkg/syscall/syscall_plan9.go

  • Note型が新しく定義され、os.Signalインターフェースを実装するようになった。
  • 以前のSignal型が削除された。
--- a/src/pkg/syscall/syscall_plan9.go
+++ b/src/pkg/syscall/syscall_plan9.go
@@ -23,6 +23,16 @@ func (e ErrorString) Error() string { return string(e) }\n // NewError converts s to an ErrorString, which satisfies the Error interface.\n func NewError(s string) { return ErrorString(s) }\n \n+// A Note is a string describing a process note.\n+// It implements the os.Signal interface.\n+type Note string\n+\n+func (n Note) Signal() {}\n+\n+func (n Note) String() string {\n+\treturn string(n)\n+}\n+\n var (\n 	Stdin  = 0\n 	Stdout = 1
@@ -322,14 +332,6 @@ func Getgroups() (gids []int, err error) {\n 	return make([]int, 0), nil\n }\n \n-type Signal int\n-\n-func (s Signal) Signal() {}\n-\n-func (s Signal) String() string {\n-\treturn ""\n-}\n-\n //sys	Dup(oldfd int, newfd int) (fd int, err error)\n //sys	Open(path string, mode int) (fd int, err error)\n //sys	Create(path string, mode int, perm uint32) (fd int, err error)\n```

### `src/pkg/syscall/zerrors_plan9_386.go`
- `SIGINT`と`SIGKILL`の定数が削除された。

```diff
--- a/src/pkg/syscall/zerrors_plan9_386.go
+++ b/src/pkg/syscall/zerrors_plan9_386.go
@@ -24,9 +24,6 @@ const (
 	S_IFREG  = 0x8000
 	S_IFLNK  = 0xa000
 	S_IFSOCK = 0xc000
-\n-\tSIGINT  = Signal(0x2)\n-\tSIGKILL = Signal(0x9)\n )
 
 // Errors

コアとなるコードの解説

このコミットの核心は、Plan 9におけるシグナル(ノート)の表現方法を、Goの型システム内でより自然で効率的なものにすることです。

  1. syscall.Noteの導入:

    • 以前はsyscall.Signalという整数ベースの型が使われていましたが、Plan 9のノートは本質的に文字列です。この不整合を解消するため、type Note stringが導入されました。
    • このNote型は、Signal() {}メソッドとString() stringメソッドを実装することで、Goのos.Signalインターフェースを満たします。これにより、osパッケージの汎用的なシグナル処理ロジックが、Plan 9のノートに対しても透過的に機能するようになります。
    • String()メソッドは、Noteの基底となる文字列値をそのまま返すため、ノートの内容を直接文字列として利用できます。
  2. os.Plan9Noteの削除:

    • syscall.Noteが直接os.Signalインターフェースを満たすようになったため、osパッケージ内でPlan 9固有のos.Plan9Note型をエクスポートする必要がなくなりました。これは、APIのクリーンアップと、OS固有の概念をより低レベルのsyscallパッケージに閉じ込めるという設計原則に合致します。
  3. os.Interruptos.KillのPlan 9固有の実装:

    • os.Interruptos.Killは、GoプログラムがOSに依存せず利用できる汎用的なシグナルです。Plan 9では、これらがそれぞれsyscall.Note("interrupt")syscall.Note("kill")として定義されました。これにより、Goの汎用シグナルがPlan 9のノートとして適切にマッピングされます。
  4. writeProcFileヘルパー関数とkillの特殊処理:

    • os/exec_plan9.goに導入されたwriteProcFile関数は、/proc/$pid/以下のファイルにデータを書き込むための汎用的なヘルパーです。これにより、コードの重複が減り、可読性が向上します。
    • (*Process).signalメソッド内でsig == Killの特殊処理が追加されたのは重要です。Plan 9では、プロセスを強制終了させる「kill」ノートは、他のノートとは異なり/proc/$pid/noteではなく/proc/$pid/ctlファイルに書き込むことで行われます。この変更は、Plan 9のシステムコール規約に厳密に従うためのものです。これにより、Goのos.KillがPlan 9上で正しく機能するようになります。

これらの変更は、Goのクロスプラットフォーム対応において、各OSの特性を尊重しつつ、共通のインターフェースを通じて一貫したプログラミングモデルを提供するというGoの設計哲学を反映しています。特に、Plan 9のノートというユニークなメカニズムを、Goの型システムとos.Signalインターフェースにうまく統合した例と言えます。

関連リンク

参考にした情報源リンク

  • 上記のGitHubコミットページ
  • Go言語の公式ドキュメント
  • Plan 9のシステムに関する一般的な情報源
  • Go言語のソースコード

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

このコミットは、Go言語のsyscallパッケージにおけるPlan 9オペレーティングシステム向けのシグナル処理に関する変更です。具体的には、既存のSignal型を廃止し、代わりにNoteという新しい型を導入しています。この変更により、エクスポートされていたos.Plan9Note型が削除され、Plan 9におけるシグナル(Goでは「ノート」と呼ばれる)の扱いがよりシンプルかつ統一されたものになります。

コミット

commit fe5005f721d3036dd3c6fa5c0e7b43c188342859
Author: Anthony Martin <ality@pbrane.org>
Date:   Fri May 4 03:44:41 2012 -0700

    syscall: remove the Signal type on Plan 9
    
    Instead use a new type, "Note", whose underlying
    type is just a string.  This allows us to
    remove the exported os.Plan9Note type.
    
    R=bradfitz, seed, rsc
    CC=golang-dev
    https://golang.org/cl/6015046

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

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

元コミット内容

Plan 9向けのsyscallパッケージからSignal型を削除し、代わりに基底型が文字列である新しいNote型を使用する。この変更により、エクスポートされていたos.Plan9Note型を削除することが可能になる。

変更の背景

Go言語は、様々なオペレーティングシステム(OS)をサポートするように設計されています。その中には、Unix系OS(Linux, macOSなど)やWindowsだけでなく、ベル研究所で開発された分散OSであるPlan 9も含まれます。

Unix系OSでは、プロセス間通信やプロセス制御のために「シグナル(Signal)」というメカニズムが広く使われています。Goのosパッケージやsyscallパッケージは、これらのシグナルを抽象化して扱うためのインターフェースを提供しています。

しかし、Plan 9ではUnixのシグナルとは異なる「ノート(Note)」という概念が使われています。ノートは、プロセスに対して文字列メッセージを送信するメカニズムであり、Unixのシグナルよりも柔軟な情報伝達が可能です。Goの初期の実装では、Plan 9のノートをUnixのシグナルインターフェースに合わせるために、syscall.Signal型やos.Plan9Note型といった抽象化レイヤーが導入されていました。

このコミットの背景には、Plan 9のノートをより自然な形でGoのシステムコール層で扱うこと、そしてUnix系OSのシグナルとPlan 9のノートという異なる概念を無理にSignalという単一の型で表現することによる複雑さや不整合を解消する意図があったと考えられます。特に、os.Plan9NoteというPlan 9固有の型がosパッケージでエクスポートされていることが、APIの統一性を損ねていた可能性があります。

この変更は、Plan 9のノートが本質的に文字列ベースであるという特性を活かし、syscallパッケージ内でNote型を文字列のエイリアスとして定義することで、よりシンプルで直感的なAPI設計を目指したものです。これにより、osパッケージからPlan 9固有の型を削除し、クロスプラットフォームなos.Signalインターフェースの背後で、各OSのシグナル/ノートの実装詳細を適切に隠蔽できるようになります。

前提知識の解説

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

  • osパッケージ: GoプログラムがOSと対話するための基本的な機能を提供します。ファイル操作、プロセス管理、環境変数へのアクセスなどが含まれます。os.Signalインターフェースは、OSシグナルを抽象的に表現するためのものです。
  • syscallパッケージ: OS固有のシステムコールへの低レベルなインターフェースを提供します。このパッケージはOSごとに異なる実装を持ち、osパッケージなどの高レベルなパッケージが内部で利用します。

2. Unix系OSにおけるシグナル (Signal)

Unix系OSでは、シグナルはソフトウェア割り込みの一種で、プロセスに特定のイベントが発生したことを通知するために使用されます。例えば、SIGINT(Ctrl+Cによる割り込み)、SIGKILL(強制終了)、SIGTERM(終了要求)などがあります。シグナルは通常、整数値で識別されます。

3. Plan 9におけるノート (Note)

Plan 9では、Unixのシグナルに代わるメカニズムとして「ノート」が導入されています。ノートは、プロセスに対して文字列メッセージを送信する機能です。これにより、単なる数値のシグナルよりも詳細な情報をプロセスに伝えることができます。例えば、プロセスが終了する際に「kill」というノートを受け取ったり、デバッガがプロセスに「debug」というノートを送ったりすることが可能です。ノートは/proc/<pid>/noteというファイルに書き込むことで送信されます。

4. /procファイルシステム

Unix系OSやPlan 9には、/procという仮想ファイルシステムが存在します。これは、実行中のプロセスに関する情報やシステムの状態をファイルとして公開するものです。各プロセスは通常、/proc/<pid>/というディレクトリを持ち、その中にプロセスのメモリマップ、ファイルディスクリプタ、そしてPlan 9の場合はノートを送信するためのnoteファイルなどが含まれます。

5. Goのインターフェース

Goのインターフェースは、メソッドのシグネチャの集合を定義する型です。ある型がインターフェースのすべてのメソッドを実装していれば、その型はそのインターフェースを満たしていると見なされます。os.Signalインターフェースは、Signal()というメソッドを持つことで、任意の型がシグナルとして扱われることを可能にします。

技術的詳細

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

  1. syscall.Signal型の削除とsyscall.Note型の導入:

    • Plan 9向けのsyscallパッケージ(src/pkg/syscall/syscall_plan9.go)から、以前定義されていたtype Signal intが削除されました。
    • 代わりに、type Note stringという新しい型が導入されました。このNote型は、os.Signalインターフェース(Signal() {})とString()メソッドを実装しています。これにより、Plan 9のノートがGoのos.Signalインターフェースを満たしつつ、その実体が文字列であることを明確に表現できるようになりました。
  2. os.Plan9Note型の削除:

    • src/pkg/os/exec.goから、os.Plan9Note型が完全に削除されました。これは、syscall.Noteが直接os.Signalインターフェースを満たすようになったため、osパッケージ側でPlan 9固有のシグナル型をエクスポートする必要がなくなったためです。
  3. os.Interruptos.KillのPlan 9実装の変更:

    • src/pkg/os/exec_plan9.goにおいて、os.Interruptos.Killの具体的な値が、以前のsyscall.SIGINTsyscall.SIGKILL(これらも削除された)から、新しく定義されたsyscall.Note("interrupt")syscall.Note("kill")に変更されました。これにより、Plan 9のノートの概念が直接的に反映されるようになりました。
  4. プロセスへのノート送信ロジックの変更:

    • src/pkg/os/exec_plan9.go(*Process).signalメソッドにおいて、プロセスにノートを送信するロジックが変更されました。以前は/proc/$pid/noteファイルに直接書き込んでいましたが、新しいwriteProcFileヘルパー関数が導入され、これを通じてノートが書き込まれるようになりました。
    • Killシグナル(ノート)の扱いが特別視され、(*Process).Kill()メソッドが直接呼び出されるようになりました。これは、Plan 9においてkillノートは/proc/$pid/noteではなく、/proc/$pid/ctlファイルに「kill」という文字列を書き込むことで実現されるためです。この変更により、Plan 9のkillメカニズムがより正確に反映されています。
  5. zerrors_plan9_386.goからのシグナル定数削除:

    • src/pkg/syscall/zerrors_plan9_386.goから、SIGINTSIGKILLの定数が削除されました。これらは以前syscall.Signal型として定義されていましたが、Signal型自体が削除されたため、不要になりました。

これらの変更により、GoのPlan 9サポートにおけるシグナル(ノート)の扱いが、よりPlan 9のネイティブなメカニズムに即したものとなり、かつGoのAPI設計の統一性が向上しました。

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

src/pkg/os/exec.go

  • Signalインターフェースの定義は残るが、InterruptKillのグローバル変数の定義が削除された。これは、OS固有のファイル(exec_plan9.goexec_posix.go)で定義されるようになったため。
--- a/src/pkg/os/exec.go
+++ b/src/pkg/os/exec.go
@@ -54,14 +54,6 @@ type Signal interface {
 	Signal() // to distinguish from other Stringers
 }
 
-// The only signal values guaranteed to be present on all systems
-// are Interrupt (send the process an interrupt) and
-// Kill (force the process to exit).\n-var (\n-\tInterrupt Signal = syscall.SIGINT\n-\tKill      Signal = syscall.SIGKILL\n-)\n-\n // Getpid returns the process id of the caller.\n func Getpid() int { return syscall.Getpid() }\n 

src/pkg/os/exec_plan9.go

  • InterruptKillsyscall.Note型として再定義された。
  • Plan9Note型が削除された。
  • writeProcFileヘルパー関数が追加された。
  • signalメソッドとkillメソッドがwriteProcFileを使用するように変更された。
--- a/src/pkg/os/exec_plan9.go
+++ b/src/pkg/os/exec_plan9.go
@@ -11,6 +11,14 @@ import (
 	"time"
 )
 
+// The only signal values guaranteed to be present on all systems
+// are Interrupt (send the process an interrupt) and Kill (force
+// the process to exit).
+var (
+	Interrupt Signal = syscall.Note("interrupt")
+	Kill      Signal = syscall.Note("kill")
+)
+
 func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err error) {
 	sysattr := &syscall.ProcAttr{
 		Dir: attr.Dir,
@@ -30,35 +38,35 @@ func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e
 	return newProcess(pid, h), nil
 }
 
-// Plan9Note implements the Signal interface on Plan 9.
-type Plan9Note string
-
-func (note Plan9Note) String() string {
-	return string(note)
+func (p *Process) writeProcFile(file string, data string) error {
+	f, e := OpenFile("/proc/"+itoa(p.Pid)+"/"+file, O_WRONLY, 0)
+	if e != nil {
+		return e
+	}
+	defer f.Close()
+	_, e = f.Write([]byte(data))
+	return e
 }
 
 func (p *Process) signal(sig Signal) error {
 	if p.done {
 		return errors.New("os: process already finished")
 	}
-
-	f, e := OpenFile("/proc/"+itoa(p.Pid)+"/note", O_WRONLY, 0)
-	if e != nil {
+	if sig == Kill {
+		// Special-case the kill signal since it doesn't use /proc/$pid/note.
+		return p.Kill()
+	}
+	if e := p.writeProcFile("note", sig.String()); e != nil {
 		return NewSyscallError("signal", e)
 	}
-	defer f.Close()
-	_, e = f.Write([]byte(sig.String()))
-	return e
+	return nil
 }
 
 func (p *Process) kill() error {
-	f, e := OpenFile("/proc/"+itoa(p.Pid)+"/ctl", O_WRONLY, 0)
-	if e != nil {
+	if e := p.writeProcFile("ctl", "kill"); e != nil {
 		return NewSyscallError("kill", e)
 	}
-	defer f.Close()
-	_, e = f.Write([]byte("kill"))
-	return e
+	return nil
 }
 
 func (p *Process) wait() (ps *ProcessState, err error) {

src/pkg/os/exec_posix.go

  • InterruptKillsyscall.SIGINTsyscall.SIGKILLとして再定義された。これは、os/exec.goから移動されたもの。
--- a/src/pkg/os/exec_posix.go
+++ b/src/pkg/os/exec_posix.go
@@ -10,6 +10,14 @@ import (
 	"syscall"
 )
 
+// The only signal values guaranteed to be present on all systems
+// are Interrupt (send the process an interrupt) and Kill (force
+// the process to exit).
+var (
+	Interrupt Signal = syscall.SIGINT
+	Kill      Signal = syscall.SIGKILL
+)
+
 func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err error) {
 	// Double-check existence of the directory we want
 	// to chdir into.  We can make the error clearer this way.

src/pkg/syscall/syscall_plan9.go

  • Note型が新しく定義され、os.Signalインターフェースを実装するようになった。
  • 以前のSignal型が削除された。
--- a/src/pkg/syscall/syscall_plan9.go
+++ b/src/pkg/syscall/syscall_plan9.go
@@ -23,6 +23,16 @@ func (e ErrorString) Error() string { return string(e) }\n // NewError converts s to an ErrorString, which satisfies the Error interface.\n func NewError(s string) { return ErrorString(s) }\n \n+// A Note is a string describing a process note.\n+// It implements the os.Signal interface.\n+type Note string\n+\n+func (n Note) Signal() {}\n+\n+func (n Note) String() string {\n+\treturn string(n)\n+}\n+\n var (\n 	Stdin  = 0\n 	Stdout = 1
@@ -322,14 +332,6 @@ func Getgroups() (gids []int, err error) {\n 	return make([]int, 0), nil
 }\n \n-type Signal int\n-\n-func (s Signal) Signal() {}\n-\n-func (s Signal) String() string {\n-\treturn ""\n-}\n-\n //sys	Dup(oldfd int, newfd int) (fd int, err error)\n //sys	Open(path string, mode int) (fd int, err error)\n //sys	Create(path string, mode int, perm uint32) (fd int, err error)\n```

### `src/pkg/syscall/zerrors_plan9_386.go`
- `SIGINT`と`SIGKILL`の定数が削除された。

```diff
--- a/src/pkg/syscall/zerrors_plan9_386.go
+++ b/src/pkg/syscall/zerrors_plan9_386.go
@@ -24,9 +24,6 @@ const (
 	S_IFREG  = 0x8000
 	S_IFLNK  = 0xa000
 	S_IFSOCK = 0xc000
-\n-\tSIGINT  = Signal(0x2)\n-\tSIGKILL = Signal(0x9)\n )
 
 // Errors

コアとなるコードの解説

このコミットの核心は、Plan 9におけるシグナル(ノート)の表現方法を、Goの型システム内でより自然で効率的なものにすることです。

  1. syscall.Noteの導入:

    • 以前はsyscall.Signalという整数ベースの型が使われていましたが、Plan 9のノートは本質的に文字列です。この不整合を解消するため、type Note stringが導入されました。
    • このNote型は、Signal() {}メソッドとString() stringメソッドを実装することで、Goのos.Signalインターフェースを満たします。これにより、osパッケージの汎用的なシグナル処理ロジックが、Plan 9のノートに対しても透過的に機能するようになります。
    • String()メソッドは、Noteの基底となる文字列値をそのまま返すため、ノートの内容を直接文字列として利用できます。
  2. os.Plan9Noteの削除:

    • syscall.Noteが直接os.Signalインターフェースを満たすようになったため、osパッケージ内でPlan 9固有のos.Plan9Note型をエクスポートする必要がなくなりました。これは、APIのクリーンアップと、OS固有の概念をより低レベルのsyscallパッケージに閉じ込めるという設計原則に合致します。
  3. os.Interruptos.KillのPlan 9固有の実装:

    • os.Interruptos.Killは、GoプログラムがOSに依存せず利用できる汎用的なシグナルです。Plan 9では、これらがそれぞれsyscall.Note("interrupt")syscall.Note("kill")として定義されました。これにより、Goの汎用シグナルがPlan 9のノートとして適切にマッピングされます。
  4. writeProcFileヘルパー関数とkillの特殊処理:

    • os/exec_plan9.goに導入されたwriteProcFile関数は、/proc/$pid/以下のファイルにデータを書き込むための汎用的なヘルパーです。これにより、コードの重複が減り、可読性が向上します。
    • (*Process).signalメソッド内でsig == Killの特殊処理が追加されたのは重要です。Plan 9では、プロセスを強制終了させる「kill」ノートは、他のノートとは異なり/proc/$pid/noteではなく/proc/$pid/ctlファイルに書き込むことで行われます。この変更は、Plan 9のシステムコール規約に厳密に従うためのものです。これにより、Goのos.KillがPlan 9上で正しく機能するようになります。

これらの変更は、Goのクロスプラットフォーム対応において、各OSの特性を尊重しつつ、共通のインターフェースを通じて一貫したプログラミングモデルを提供するというGoの設計哲学を反映しています。特に、Plan 9のノートというユニークなメカニズムを、Goの型システムとos.Signalインターフェースにうまく統合した例と言えます。

関連リンク

参考にした情報源リンク

  • 上記のGitHubコミットページ
  • Go言語の公式ドキュメント
  • Plan 9のシステムに関する一般的な情報源
  • Go言語のソースコード
  • Web検索結果: "In Plan 9, the concept of "signals" as known in Unix-like systems is replaced by "notes." Unlike Unix signals, which are typically integer values, Plan 9 notes are arbitrary strings that can be sent between processes. This allows for more descriptive and flexible inter-process communication. In the Go programming language, when running on a Plan 9 system, the os/signal package adapts to this system. Specifically, a "Signal Note type" refers to the syscall.Note type, which is a string. When you use signal.Notify in Go on Plan 9, you can register to receive these string-based notes. If a process posts a note that matches the string you've registered, that string value will be sent on the Go channel." (ycombinator.com, github.io, wikipedia.org, go.dev)