[インデックス 11992] ファイルの概要
このコミットは、Goランタイムが特定のUnixシグナル(SIGTSTP
, SIGTTIN
, SIGTTOU
)のデフォルトの振る舞いを許可するように変更するものです。これにより、Goプログラムがこれらのシグナルを受信した際に、オペレーティングシステムが本来持つデフォルトの処理(例えば、SIGTSTP
によるプロセスの一時停止)が行われるようになります。これは、Goプログラムがこれらのシグナルを捕捉して独自の処理を行う必要がない場合に、より自然なシステム挙動を可能にするための改善です。
コミット
commit 3d8ebefbbe5c271b9b97904d87c4fa970c035f17
Author: David Symonds <dsymonds@golang.org>
Date: Fri Feb 17 14:36:40 2012 +1100
runtime: Permit default behaviour of SIGTSTP, SIGTTIN, SIGTTOU.
Fixes #3037.
R=rsc, minux.ma, r, rsc
CC=golang-dev
https://golang.org/cl/5674072
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/3d8ebefbbe5c271b9b97904d87c4fa970c035f17
元コミット内容
Goランタイムにおいて、SIGTSTP
、SIGTTIN
、SIGTTOU
といったシグナルのデフォルトの振る舞いを許可するように変更します。この変更は、Issue #3037を修正するものです。
変更の背景
この変更の背景には、Goプログラムが特定のシグナル(SIGTSTP
, SIGTTIN
, SIGTTOU
)を受信した際に、Goランタイムがこれらのシグナルを捕捉し、デフォルトのOSの振る舞いを妨げてしまうという問題がありました。
具体的には、Goプログラムがフォアグラウンドで実行されている際に、ユーザーがCtrl+Zを押してプロセスを一時停止しようとすると(SIGTSTP
)、Goランタイムがこのシグナルを捕捉してしまい、プロセスが一時停止しない、あるいは予期せぬ挙動を示すことがありました。同様に、バックグラウンドのプロセスが端末からの入力(SIGTTIN
)や出力(SIGTTOU
)を試みた際に、これらのシグナルがGoランタイムによって処理され、本来OSがプロセスを停止させるべき状況で停止しない、といった問題が発生していました。
Issue #3037では、この問題が報告されており、Goプログラムがこれらのシグナルに対してOSのデフォルトの振る舞いを尊重すべきであるという議論がありました。このコミットは、その問題を解決し、GoプログラムがよりUnixライクなシグナル処理の挙動を示すようにするためのものです。
前提知識の解説
Unixシグナル
Unix系OSにおいて、シグナルはプロセス間通信やプロセス制御のためのソフトウェア割り込みの一種です。OSは特定のイベント(例: Ctrl+Cによる割り込み、子プロセスの終了、不正なメモリアクセスなど)が発生した際に、関連するプロセスにシグナルを送信します。プロセスはシグナルを受信すると、以下のいずれかの方法で応答します。
- デフォルトの振る舞い: OSがシグナルに対して定義している標準的なアクションを実行します(例: プロセスを終了する、コアダンプを生成する、プロセスを一時停止する)。
- シグナルハンドラのインストール: プログラムが特定のシグナルを受信した際に実行されるカスタム関数(シグナルハンドラ)を登録します。これにより、デフォルトの振る舞いを上書きし、独自の処理を行うことができます。
- シグナルの無視: 特定のシグナルを無視するように設定します。ただし、
SIGKILL
やSIGSTOP
など、一部のシグナルは無視できません。
端末制御シグナル
このコミットで扱われるSIGTSTP
, SIGTTIN
, SIGTTOU
は、特に端末(TTY)制御に関連するシグナルです。
SIGTSTP
(Terminal Stop):- 通常、ユーザーがキーボードでCtrl+Zを押したときに、フォアグラウンドのプロセスグループに送信されます。
- デフォルトの振る舞いは、プロセスを一時停止(サスペンド)させることです。これにより、ユーザーは
fg
コマンドでプロセスをフォアグラウンドに戻したり、bg
コマンドでバックグラウンドで実行を再開させたりできます。
SIGTTIN
(Terminal Input):- バックグラウンドのプロセスが、制御端末からの入力を読み取ろうとしたときに、そのプロセスグループに送信されます。
- デフォルトの振る舞いは、プロセスを一時停止させることです。これは、バックグラウンドプロセスがユーザーの介入なしに端末入力をブロックするのを防ぐためです。
SIGTTOU
(Terminal Output):- バックグラウンドのプロセスが、制御端末に出力しようとしたときに、そのプロセスグループに送信されます。
- デフォルトの振る舞いは、プロセスを一時停止させることです。これは、バックグラウンドプロセスがユーザーの介入なしに端末出力を乱すのを防ぐためです。
Goランタイムのシグナルハンドリング
Goランタイムは、プログラムの実行を管理し、ガベージコレクションやゴルーチンのスケジューリングなどを行います。これには、OSからのシグナルを処理するメカニズムも含まれます。Goのos/signal
パッケージを使用すると、Goプログラム内で特定のシグナルを捕捉し、Goのチャネルを通じて通知を受け取ることができます。しかし、ランタイム自体が内部的にシグナルを処理する方法は、ユーザーが明示的にos/signal
パッケージを使用しない場合でも、プログラムの挙動に影響を与えます。
以前のGoランタイムでは、これらの端末制御シグナルもランタイムが捕捉し、デフォルトのOSの振る舞いを上書きしてしまうことがありました。このコミットは、この挙動を修正し、特定のシグナルについてはOSのデフォルトの振る舞いを優先するように変更します。
技術的詳細
このコミットの主要な技術的変更点は、Goランタイムのシグナル処理メカニズムにSigDefault
という新しいフラグを導入し、特定のシグナル(SIGTSTP
, SIGTTIN
, SIGTTOU
)にこのフラグを設定することです。
Goランタイムは、内部的にSigTab
という構造体配列を使用して、各シグナルに関する情報(シグナル番号、名前、およびそのシグナルに対するランタイムの振る舞いを制御するフラグ)を管理しています。
-
SigDefault
フラグの導入:src/pkg/runtime/runtime.h
において、SigDefault = 1<<4
という新しい列挙値が追加されました。このフラグは、「シグナルが明示的に要求されていない場合(つまり、os/signal
パッケージなどでGoプログラムがこのシグナルを捕捉しようとしていない場合)、ランタイムはこのシグナルを監視せず、OSのデフォルトの振る舞いを許可する」ことを示します。 -
runtime·sigenable
関数の追加: Goランタイムは、runtime·initsig
関数で初期シグナルハンドラを設定します。しかし、このコミットでは、runtime·sigenable
という新しい関数が導入されました。この関数は、特定のシグナルがSigDefault
フラグを持っている場合、そのシグナルに対してOSのデフォルトハンドラを有効にする役割を担います。src/pkg/runtime/signal_unix.c
にその実装が追加され、signal_plan9_386.c
、signal_windows_386.c
、signal_windows_amd64.c
にはプラットフォーム固有のスタブが追加されています。
-
SigTab
の更新: 各OS(Darwin, FreeBSD, Linux, NetBSD, OpenBSD)のシグナル定義ファイル(例:src/pkg/runtime/signals_darwin.h
)において、SIGTSTP
,SIGTTIN
,SIGTTOU
のSigTab
エントリに+D
(SigDefault
の略)が追加されました。これにより、これらのシグナルがSigDefault
の特性を持つことがランタイムに伝えられます。 -
runtime·initsig
の変更:src/pkg/runtime/signal_unix.c
のruntime·initsig
関数が変更され、シグナルハンドラを設定する際に、SigDefault
フラグが設定されているシグナルは初期設定ではGoランタイムのカスタムハンドラを設定しないようになりました。これにより、これらのシグナルはOSのデフォルトの振る舞いを維持します。 -
signal_enable
の変更:src/pkg/runtime/sigqueue.goc
のsignal_enable
関数(Goのos/signal
パッケージが内部的に使用する関数)が変更され、Goプログラムが明示的に特定のシグナルを捕捉しようとした場合(sig.wanted
にそのシグナルが追加された場合)に、runtime·sigenable
を呼び出すようになりました。これにより、もしそのシグナルがSigDefault
としてマークされていたとしても、Goプログラムがそれを捕捉したいと表明した場合には、ランタイムがそのシグナルを監視するように切り替わります。
これらの変更により、GoランタイムはデフォルトではSIGTSTP
, SIGTTIN
, SIGTTOU
のOSデフォルトの振る舞いを尊重し、Goプログラムがこれらのシグナルを明示的に処理したい場合にのみ、ランタイムが介入するようになります。
コアとなるコードの変更箇所
src/pkg/runtime/runtime.h
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -268,9 +268,10 @@ struct SigTab
enum
{
SigNotify = 1<<0, // let signal.Notify have signal, even if from kernel
- SigKill = 1<<1, // if signal.Notify doesn\'t take it, exit quietly
- SigThrow = 1<<2, // if signal.Notify doesn\'t take it, exit loudly
- SigPanic = 1<<3, // if the signal is from the kernel, panic
+ SigKill = 1<<1, // if signal.Notify doesn\'t take it, exit quietly
+ SigThrow = 1<<2, // if signal.Notify doesn\'t take it, exit loudly
+ SigPanic = 1<<3, // if the signal is from the kernel, panic
+ SigDefault = 1<<4, // if the signal isn\'t explicitly requested, don\'t monitor it
};
// NOTE(rsc): keep in sync with extern.go:/type.Func.
@@ -501,6 +502,7 @@ Slice runtime·gobytes(byte*, int32);\n String runtime·gostringnocopy(byte*);\n String runtime·gostringw(uint16*);\n void runtime·initsig(void);\n+void runtime·sigenable(uint32 sig);\n int32 runtime·gotraceback(void);\n void runtime·goroutineheader(G*);\n void runtime·traceback(uint8 *pc, uint8 *sp, uint8 *lr, G* gp);\n```
### `src/pkg/runtime/signal_unix.c`
```diff
--- a/src/pkg/runtime/signal_unix.c
+++ b/src/pkg/runtime/signal_unix.c
@@ -27,9 +27,27 @@ runtime·initsig(void)\n // First call: basic setup.\n for(i = 0; i<NSIG; i++) {\n t = &runtime·sigtab[i];\n- if(t->flags == 0)\n+ if((t->flags == 0) || (t->flags & SigDefault))\n continue;\n- runtime·setsig(i, runtime·sighandler, 1);\n+ runtime·setsig(i, runtime·sighandler, true);\n+ }\n+}\n+\n+void\n+runtime·sigenable(uint32 sig)\n+{\n+ int32 i;\n+ SigTab *t;\n+\n+ for(i = 0; i<NSIG; i++) {\n+ // ~0 means all signals.\n+ if(~sig == 0 || i == sig) {\n+ t = &runtime·sigtab[i];\n+ if(t->flags & SigDefault) {\n+ runtime·setsig(i, runtime·sighandler, true);\n+ t->flags &= ~SigDefault; // make this idempotent\n+ }\n+ }\n }\n }\n \n```
### `src/pkg/runtime/signals_linux.h` (他のOSのsignals_*.hも同様)
```diff
--- a/src/pkg/runtime/signals_linux.h
+++ b/src/pkg/runtime/signals_linux.h
@@ -6,6 +6,7 @@
#define K SigKill
#define T SigThrow
#define P SigPanic
+#define D SigDefault
SigTab runtime·sigtab[] = {
/* 0 */ 0, "SIGNONE: no trap",
@@ -28,9 +29,9 @@ SigTab runtime·sigtab[] = {
/* 17 */ N, "SIGCHLD: child status has changed",
/* 18 */ 0, "SIGCONT: continue",
/* 19 */ 0, "SIGSTOP: stop, unblockable",
- /* 20 */ N, "SIGTSTP: keyboard stop",
- /* 21 */ N, "SIGTTIN: background read from tty",
- /* 22 */ N, "SIGTTOU: background write to tty",
+ /* 20 */ N+D, "SIGTSTP: keyboard stop",
+ /* 21 */ N+D, "SIGTTIN: background read from tty",
+ /* 22 */ N+D, "SIGTTOU: background write to tty",
/* 23 */ N, "SIGURG: urgent condition on socket",
/* 24 */ N, "SIGXCPU: cpu limit exceeded",
/* 25 */ N, "SIGXFSZ: file size limit exceeded",
@@ -79,3 +80,4 @@ SigTab runtime·sigtab[] = {
#undef K
#undef T
#undef P
+#undef D
src/pkg/runtime/sigqueue.goc
--- a/src/pkg/runtime/sigqueue.goc
+++ b/src/pkg/runtime/sigqueue.goc
@@ -140,10 +140,12 @@ func signal_enable(s uint32) {
// Special case: want everything.
for(i=0; i<nelem(sig.wanted); i++)
sig.wanted[i] = ~(uint32)0;
+\truntime·sigenable(s);\
return;
}
if(s >= nelem(sig.wanted)*32)
return;
sig.wanted[s/32] |= 1U<<(s&31);\
+\truntime·sigenable(s);\
}\n```
## コアとなるコードの解説
### `src/pkg/runtime/runtime.h`の変更
* **`SigDefault = 1<<4`の追加**: これは、Goランタイムがシグナルを明示的に処理しない場合に、OSのデフォルトの振る舞いを許可するための新しいフラグです。これにより、ランタイムが不必要にシグナルを捕捉するのを防ぎます。
* **`void runtime·sigenable(uint32 sig);`の追加**: この関数は、特定のシグナルに対してOSのデフォルトハンドラを有効にするためのものです。Goプログラムが`os/signal`パッケージを通じてシグナルを捕捉しようとした際に、この関数が呼び出され、必要に応じてシグナルハンドラが再設定されます。
### `src/pkg/runtime/signal_unix.c`の変更
* **`runtime·initsig`の変更**:
`if((t->flags == 0) || (t->flags & SigDefault))`という条件が追加されました。これは、シグナルに`SigDefault`フラグが設定されている場合、初期化時にGoランタイムのカスタムシグナルハンドラ(`runtime·sighandler`)を設定しないことを意味します。これにより、これらのシグナルはOSのデフォルトの振る舞いを維持します。
* **`runtime·sigenable`関数の実装**:
この関数は、引数で指定されたシグナル(またはすべてのシグナル)に対して、`SigDefault`フラグが設定されている場合に、`runtime·setsig`を呼び出してGoランタイムのシグナルハンドラを有効にします。そして、`SigDefault`フラグをクリアすることで、この処理が冪等になるようにします。これは、Goプログラムが後から`os/signal`パッケージでこれらのシグナルを捕捉しようとしたときに、ランタイムが適切に介入できるようにするために重要です。
### `src/pkg/runtime/signals_linux.h` (および他のOSのsignals_*.h)の変更
* **`#define D SigDefault`の追加**: `SigDefault`フラグを簡潔に参照するためのマクロです。
* **`SIGTSTP`, `SIGTTIN`, `SIGTTOU`の`SigTab`エントリへの`+D`の追加**:
例えば、`/* 20 */ N+D, "SIGTSTP: keyboard stop"`のように変更されています。これは、これらのシグナルが`SigDefault`の特性を持つことを明示的に示しています。これにより、`runtime·initsig`はこれらのシグナルに対して初期ハンドラを設定せず、OSのデフォルトの振る舞いを許可します。
### `src/pkg/runtime/sigqueue.goc`の変更
* **`runtime·sigenable(s);`の追加**:
`signal_enable`関数は、Goプログラムが`os/signal`パッケージを通じて特定のシグナルを捕捉しようとしたときに呼び出されます。この変更により、シグナルが`sig.wanted`に追加された後、`runtime·sigenable(s)`が呼び出されます。これにより、もしそのシグナルが以前に`SigDefault`としてマークされていたとしても、Goプログラムがそれを明示的に処理したいと表明した場合には、ランタイムがそのシグナルを監視するように切り替わり、Goのシグナルハンドリングメカニズムが機能するようになります。
これらの変更の組み合わせにより、Goランタイムはデフォルトで端末制御シグナル(`SIGTSTP`, `SIGTTIN`, `SIGTTOU`)のOSデフォルトの振る舞いを尊重し、Goプログラムがこれらのシグナルを明示的に処理したい場合にのみ、ランタイムが介入する、というより柔軟で期待される挙動を実現しています。
## 関連リンク
* Go Issue #3037: [https://github.com/golang/go/issues/3037](https://github.com/golang/go/issues/3037)
* Go CL 5674072: [https://golang.org/cl/5674072](https://golang.org/cl/5674072)
## 参考にした情報源リンク
* Unix Signals: [https://en.wikipedia.org/wiki/Unix_signal](https://en.wikipedia.org/wiki/Unix_signal)
* `SIGTSTP`, `SIGTTIN`, `SIGTTOU`に関する情報: [https://www.gnu.org/software/libc/manual/html_node/Job-Control-Signals.html](https://www.gnu.org/software/libc/manual/html_node/Job-Control-Signals.html)
* Goのシグナルハンドリングに関するドキュメント(`os/signal`パッケージ): [https://pkg.go.dev/os/signal](https://pkg.go.dev/os/signal)
* Go runtime signal handling (general concepts): [https://go.dev/src/runtime/signal_unix.go](https://go.dev/src/runtime/signal_unix.go) (現在のGoのソースコードは変更されている可能性がありますが、概念理解に役立ちます)
* Go runtime source code (for context on `SigTab`, `runtime·setsig`, etc.): [https://go.dev/src/runtime/](https://go.dev/src/runtime/)# [インデックス 11992] ファイルの概要
このコミットは、Goランタイムが特定のUnixシグナル(`SIGTSTP`, `SIGTTIN`, `SIGTTOU`)のデフォルトの振る舞いを許可するように変更するものです。これにより、Goプログラムがこれらのシグナルを受信した際に、オペレーティングシステムが本来持つデフォルトの処理(例えば、`SIGTSTP`によるプロセスの一時停止)が行われるようになります。これは、Goプログラムがこれらのシグナルを捕捉して独自の処理を行う必要がない場合に、より自然なシステム挙動を可能にするための改善です。
## コミット
commit 3d8ebefbbe5c271b9b97904d87c4fa970c035f17 Author: David Symonds dsymonds@golang.org Date: Fri Feb 17 14:36:40 2012 +1100
runtime: Permit default behaviour of SIGTSTP, SIGTTIN, SIGTTOU.
Fixes #3037.
R=rsc, minux.ma, r, rsc
CC=golang-dev
https://golang.org/cl/5674072
## GitHub上でのコミットページへのリンク
[https://github.com/golang/go/commit/3d8ebefbbe5c271b9b97904d87c4fa970c035f17](https://github.com/golang.com/go/commit/3d8ebefbbe5c271b9b97904d87c4fa970c035f17)
## 元コミット内容
Goランタイムにおいて、`SIGTSTP`、`SIGTTIN`、`SIGTTOU`といったシグナルのデフォルトの振る舞いを許可するように変更します。この変更は、Issue #3037を修正するものです。
## 変更の背景
この変更の背景には、Goプログラムが特定のシグナル(`SIGTSTP`, `SIGTTIN`, `SIGTTOU`)を受信した際に、Goランタイムがこれらのシグナルを捕捉し、デフォルトのOSの振る舞いを妨げてしまうという問題がありました。
具体的には、Goプログラムがフォアグラウンドで実行されている際に、ユーザーがCtrl+Zを押してプロセスを一時停止しようとすると(`SIGTSTP`)、Goランタイムがこのシグナルを捕捉してしまい、プロセスが一時停止しない、あるいは予期せぬ挙動を示すことがありました。同様に、バックグラウンドのプロセスが端末からの入力(`SIGTTIN`)や出力(`SIGTTOU`)を試みた際に、これらのシグナルがGoランタイムによって処理され、本来OSがプロセスを停止させるべき状況で停止しない、といった問題が発生していました。
Issue #3037では、この問題が報告されており、Goプログラムがこれらのシグナルに対してOSのデフォルトの振る舞いを尊重すべきであるという議論がありました。このコミットは、その問題を解決し、GoプログラムがよりUnixライクなシグナル処理の挙動を示すようにするためのものです。
## 前提知識の解説
### Unixシグナル
Unix系OSにおいて、シグナルはプロセス間通信やプロセス制御のためのソフトウェア割り込みの一種です。OSは特定のイベント(例: Ctrl+Cによる割り込み、子プロセスの終了、不正なメモリアクセスなど)が発生した際に、関連するプロセスにシグナルを送信します。プロセスはシグナルを受信すると、以下のいずれかの方法で応答します。
1. **デフォルトの振る舞い:** OSがシグナルに対して定義している標準的なアクションを実行します(例: プロセスを終了する、コアダンプを生成する、プロセスを一時停止する)。
2. **シグナルハンドラのインストール:** プログラムが特定のシグナルを受信した際に実行されるカスタム関数(シグナルハンドラ)を登録します。これにより、デフォルトの振る舞いを上書きし、独自の処理を行うことができます。
3. **シグナルの無視:** 特定のシグナルを無視するように設定します。ただし、`SIGKILL`や`SIGSTOP`など、一部のシグナルは無視できません。
### 端末制御シグナル
このコミットで扱われる`SIGTSTP`, `SIGTTIN`, `SIGTTOU`は、特に端末(TTY)制御に関連するシグナルです。
* **`SIGTSTP` (Terminal Stop):**
* 通常、ユーザーがキーボードでCtrl+Zを押したときに、フォアグラウンドのプロセスグループに送信されます。
* デフォルトの振る舞いは、プロセスを一時停止(サスペンド)させることです。これにより、ユーザーは`fg`コマンドでプロセスをフォアグラウンドに戻したり、`bg`コマンドでバックグラウンドで実行を再開させたりできます。
* **`SIGTTIN` (Terminal Input):**
* バックグラウンドのプロセスが、制御端末からの入力を読み取ろうとしたときに、そのプロセスグループに送信されます。
* デフォルトの振る舞いは、プロセスを一時停止させることです。これは、バックグラウンドプロセスがユーザーの介入なしに端末入力をブロックするのを防ぐためです。
* **`SIGTTOU` (Terminal Output):**
* バックグラウンドのプロセスが、制御端末に出力しようとしたときに、そのプロセスグループに送信されます。
* デフォルトの振る舞いは、プロセスを一時停止させることです。これは、バックグラウンドプロセスがユーザーの介入なしに端末出力を乱すのを防ぐためです。
### Goランタイムのシグナルハンドリング
Goランタイムは、プログラムの実行を管理し、ガベージコレクションやゴルーチンのスケジューリングなどを行います。これには、OSからのシグナルを処理するメカニズムも含まれます。Goの`os/signal`パッケージを使用すると、Goプログラム内で特定のシグナルを捕捉し、Goのチャネルを通じて通知を受け取ることができます。しかし、ランタイム自体が内部的にシグナルを処理する方法は、ユーザーが明示的に`os/signal`パッケージを使用しない場合でも、プログラムの挙動に影響を与えます。
以前のGoランタイムでは、これらの端末制御シグナルもランタイムが捕捉し、デフォルトのOSの振る舞いを上書きしてしまうことがありました。このコミットは、この挙動を修正し、特定のシグナルについてはOSのデフォルトの振る舞いを優先するように変更します。
## 技術的詳細
このコミットの主要な技術的変更点は、Goランタイムのシグナル処理メカニズムに`SigDefault`という新しいフラグを導入し、特定のシグナル(`SIGTSTP`, `SIGTTIN`, `SIGTTOU`)にこのフラグを設定することです。
Goランタイムは、内部的に`SigTab`という構造体配列を使用して、各シグナルに関する情報(シグナル番号、名前、およびそのシグナルに対するランタイムの振る舞いを制御するフラグ)を管理しています。
1. **`SigDefault`フラグの導入:**
`src/pkg/runtime/runtime.h`において、`SigDefault = 1<<4`という新しい列挙値が追加されました。このフラグは、「シグナルが明示的に要求されていない場合(つまり、`os/signal`パッケージなどでGoプログラムがこのシグナルを捕捉しようとしていない場合)、ランタイムはこのシグナルを監視せず、OSのデフォルトの振る舞いを許可する」ことを示します。
2. **`runtime·sigenable`関数の追加:**
Goランタイムは、`runtime·initsig`関数で初期シグナルハンドラを設定します。しかし、このコミットでは、`runtime·sigenable`という新しい関数が導入されました。この関数は、特定のシグナルが`SigDefault`フラグを持っている場合、そのシグナルに対してOSのデフォルトハンドラを有効にする役割を担います。
* `src/pkg/runtime/signal_unix.c`にその実装が追加され、`signal_plan9_386.c`、`signal_windows_386.c`、`signal_windows_amd64.c`にはプラットフォーム固有のスタブが追加されています。
3. **`SigTab`の更新:**
各OS(Darwin, FreeBSD, Linux, NetBSD, OpenBSD)のシグナル定義ファイル(例: `src/pkg/runtime/signals_darwin.h`)において、`SIGTSTP`, `SIGTTIN`, `SIGTTOU`の`SigTab`エントリに`+D`(`SigDefault`の略)が追加されました。これにより、これらのシグナルが`SigDefault`の特性を持つことがランタイムに伝えられます。
4. **`runtime·initsig`の変更:**
`src/pkg/runtime/signal_unix.c`の`runtime·initsig`関数が変更され、シグナルハンドラを設定する際に、`SigDefault`フラグが設定されているシグナルは初期設定ではGoランタイムのカスタムハンドラを設定しないようになりました。これにより、これらのシグナルはOSのデフォルトの振る舞いを維持します。
5. **`signal_enable`の変更:**
`src/pkg/runtime/sigqueue.goc`の`signal_enable`関数(Goの`os/signal`パッケージが内部的に使用する関数)が変更され、Goプログラムが明示的に特定のシグナルを捕捉しようとした場合(`sig.wanted`にそのシグナルが追加された場合)に、`runtime·sigenable`を呼び出すようになりました。これにより、もしそのシグナルが`SigDefault`としてマークされていたとしても、Goプログラムがそれを捕捉したいと表明した場合には、ランタイムがそのシグナルを監視するように切り替わります。
これらの変更により、Goランタイムはデフォルトでは`SIGTSTP`, `SIGTTIN`, `SIGTTOU`のOSデフォルトの振る舞いを尊重し、Goプログラムがこれらのシグナルを明示的に処理したい場合にのみ、ランタイムが介入するようになります。
## コアとなるコードの変更箇所
### `src/pkg/runtime/runtime.h`
```diff
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -268,9 +268,10 @@ struct SigTab
enum
{
SigNotify = 1<<0, // let signal.Notify have signal, even if from kernel
- SigKill = 1<<1, // if signal.Notify doesn\'t take it, exit quietly
- SigThrow = 1<<2, // if signal.Notify doesn\'t take it, exit loudly
- SigPanic = 1<<3, // if the signal is from the kernel, panic
+ SigKill = 1<<1, // if signal.Notify doesn\'t take it, exit quietly
+ SigThrow = 1<<2, // if signal.Notify doesn\'t take it, exit loudly
+ SigPanic = 1<<3, // if the signal is from the kernel, panic
+ SigDefault = 1<<4, // if the signal isn\'t explicitly requested, don\'t monitor it
};
// NOTE(rsc): keep in sync with extern.go:/type.Func.
@@ -501,6 +502,7 @@ Slice runtime·gobytes(byte*, int32);\n String runtime·gostringnocopy(byte*);\n String runtime·gostringw(uint16*);\n void runtime·initsig(void);\n+void runtime·sigenable(uint32 sig);\n int32 runtime·gotraceback(void);\n void runtime·goroutineheader(G*);\n void runtime·traceback(uint8 *pc, uint8 *sp, uint8 *lr, G* gp);\n```
### `src/pkg/runtime/signal_unix.c`
```diff
--- a/src/pkg/runtime/signal_unix.c
+++ b/src/pkg/runtime/signal_unix.c
@@ -27,9 +27,27 @@ runtime·initsig(void)\n // First call: basic setup.\n for(i = 0; i<NSIG; i++) {\n t = &runtime·sigtab[i];\n- if(t->flags == 0)\n+ if((t->flags == 0) || (t->flags & SigDefault))\n continue;\n- runtime·setsig(i, runtime·sighandler, 1);\n+ runtime·setsig(i, runtime·sighandler, true);\n+ }\n+}\n+\n+void\n+runtime·sigenable(uint32 sig)\n+{\n+ int32 i;\n+ SigTab *t;\n+\n+ for(i = 0; i<NSIG; i++) {\n+ // ~0 means all signals.\n+ if(~sig == 0 || i == sig) {\n+ t = &runtime·sigtab[i];\n+ if(t->flags & SigDefault) {\n+ runtime·setsig(i, runtime·sighandler, true);\n+ t->flags &= ~SigDefault; // make this idempotent\n+ }\n+ }\n }\n }\n \n```
### `src/pkg/runtime/signals_linux.h` (他のOSのsignals_*.hも同様)
```diff
--- a/src/pkg/runtime/signals_linux.h
+++ b/src/pkg/runtime/signals_linux.h
@@ -6,6 +6,7 @@
#define K SigKill
#define T SigThrow
#define P SigPanic
+#define D SigDefault
SigTab runtime·sigtab[] = {
/* 0 */ 0, "SIGNONE: no trap",
@@ -28,9 +29,9 @@ SigTab runtime·sigtab[] = {
/* 17 */ N, "SIGCHLD: child status has changed",
/* 18 */ 0, "SIGCONT: continue",
/* 19 */ 0, "SIGSTOP: stop, unblockable",
- /* 20 */ N, "SIGTSTP: keyboard stop",
- /* 21 */ N, "SIGTTIN: background read from tty",
- /* 22 */ N, "SIGTTOU: background write to tty",
+ /* 20 */ N+D, "SIGTSTP: keyboard stop",
+ /* 21 */ N+D, "SIGTTIN: background read from tty",
+ /* 22 */ N+D, "SIGTTOU: background write to tty",
/* 23 */ N, "SIGURG: urgent condition on socket",
/* 24 */ N, "SIGXCPU: cpu limit exceeded",
/* 25 */ N, "SIGXFSZ: file size limit exceeded",
@@ -79,3 +80,4 @@ SigTab runtime·sigtab[] = {
#undef K
#undef T
#undef P
+#undef D
src/pkg/runtime/sigqueue.goc
--- a/src/pkg/runtime/sigqueue.goc
+++ b/src/pkg/runtime/sigqueue.goc
@@ -140,10 +140,12 @@ func signal_enable(s uint32) {
// Special case: want everything.
for(i=0; i<nelem(sig.wanted); i++)
sig.wanted[i] = ~(uint32)0;
+\truntime·sigenable(s);\
return;
}
if(s >= nelem(sig.wanted)*32)
return;
sig.wanted[s/32] |= 1U<<(s&31);\
+\truntime·sigenable(s);\
}\n```
## コアとなるコードの解説
### `src/pkg/runtime/runtime.h`の変更
* **`SigDefault = 1<<4`の追加**: これは、Goランタイムがシグナルを明示的に処理しない場合に、OSのデフォルトの振る舞いを許可するための新しいフラグです。これにより、ランタイムが不必要にシグナルを捕捉するのを防ぎます。
* **`void runtime·sigenable(uint32 sig);`の追加**: この関数は、特定のシグナルに対してOSのデフォルトハンドラを有効にするためのものです。Goプログラムが`os/signal`パッケージを通じてシグナルを捕捉しようとした際に、この関数が呼び出され、必要に応じてシグナルハンドラが再設定されます。
### `src/pkg/runtime/signal_unix.c`の変更
* **`runtime·initsig`の変更**:
`if((t->flags == 0) || (t->flags & SigDefault))`という条件が追加されました。これは、シグナルに`SigDefault`フラグが設定されている場合、初期化時にGoランタイムのカスタムシグナルハンドラ(`runtime·sighandler`)を設定しないことを意味します。これにより、これらのシグナルはOSのデフォルトの振る舞いを維持します。
* **`runtime·sigenable`関数の実装**:
この関数は、引数で指定されたシグナル(またはすべてのシグナル)に対して、`SigDefault`フラグが設定されている場合に、`runtime·setsig`を呼び出してGoランタイムのシグナルハンドラを有効にします。そして、`SigDefault`フラグをクリアすることで、この処理が冪等になるようにします。これは、Goプログラムが後から`os/signal`パッケージでこれらのシグナルを捕捉しようとしたときに、ランタイムが適切に介入できるようにするために重要です。
### `src/pkg/runtime/signals_linux.h` (および他のOSのsignals_*.h)の変更
* **`#define D SigDefault`の追加**: `SigDefault`フラグを簡潔に参照するためのマクロです。
* **`SIGTSTP`, `SIGTTIN`, `SIGTTOU`の`SigTab`エントリへの`+D`の追加**:
例えば、`/* 20 */ N+D, "SIGTSTP: keyboard stop"`のように変更されています。これは、これらのシグナルが`SigDefault`の特性を持つことを明示的に示しています。これにより、`runtime·initsig`はこれらのシグナルに対して初期ハンドラを設定せず、OSのデフォルトの振る舞いを許可します。
### `src/pkg/runtime/sigqueue.goc`の変更
* **`runtime·sigenable(s);`の追加**:
`signal_enable`関数は、Goプログラムが`os/signal`パッケージを通じて特定のシグナルを捕捉しようとしたときに呼び出されます。この変更により、シグナルが`sig.wanted`に追加された後、`runtime·sigenable(s)`が呼び出されます。これにより、もしそのシグナルが以前に`SigDefault`としてマークされていたとしても、Goプログラムがそれを明示的に処理したいと表明した場合には、ランタイムがそのシグナルを監視するように切り替わり、Goのシグナルハンドリングメカニズムが機能するようになります。
これらの変更の組み合わせにより、Goランタイムはデフォルトで端末制御シグナル(`SIGTSTP`, `SIGTTIN`, `SIGTTOU`)のOSデフォルトの振る舞いを尊重し、Goプログラムがこれらのシグナルを明示的に処理したい場合にのみ、ランタイムが介入する、というより柔軟で期待される挙動を実現しています。
## 関連リンク
* Go Issue #3037: [https://github.com/golang/go/issues/3037](https://github.com/golang/go/issues/3037)
* Go CL 5674072: [https://golang.org/cl/5674072](https://golang.org/cl/5674072)
## 参考にした情報源リンク
* Unix Signals: [https://en.wikipedia.org/wiki/Unix_signal](https://en.wikipedia.org/wiki/Unix_signal)
* `SIGTSTP`, `SIGTTIN`, `SIGTTOU`に関する情報: [https://www.gnu.org/software/libc/manual/html_node/Job-Control-Signals.html](https://www.gnu.org/software/libc/manual/html_node/Job-Control-Signals.html)
* Goのシグナルハンドリングに関するドキュメント(`os/signal`パッケージ): [https://pkg.go.dev/os/signal](https://pkg.go.dev/os/signal)
* Go runtime signal handling (general concepts): [https://go.dev/src/runtime/signal_unix.go](https://go.dev/src/runtime/signal_unix.go) (現在のGoのソースコードは変更されている可能性がありますが、概念理解に役立ちます)
* Go runtime source code (for context on `SigTab`, `runtime·setsig`, etc.): [https://go.dev/src/runtime/](https://go.dev/src/runtime/)