TL;DR
- シェル(例えば
bash
)上でCtrl-C
などを実行すると、プロセスに対してシグナルが送られる - プロセスは、受け取ったシグナルに対して応答するだけ
- 多くのものは、そのまま終了する
- シグナルは、ハンドリングすることができる
-
SIGKILL
除く
-
- シェル(というか端末)上で、どんな操作を行うとシグナルを送ったりすることができるのかは、
stty
コマンドで確認できる- 必ずしもシグナルが送られるわけではない(
Ctrl-D
など)
- 必ずしもシグナルが送られるわけではない(
ということについて、つらつらと書いてみようかなと。
Ctrl-C
を使うと実行中のプロセスを停止できたりしますが、あれはシグナルを送ってるんですよ、という話。
Ctrl-Z
だと、サスペンドになりますね。
Ctrl-D
だと、bash
が終了しますね。あれはなんなんでしょう?
環境
今回の環境は、こちら。
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 18.04.3 LTS
Release: 18.04
Codename: bionic
$ uname -a
Linux xxxxx 4.15.0-60-generic #67-Ubuntu SMP Thu Aug 22 16:55:30 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
Ubuntu Linux 18.04 LTS。
シグナルの一覧と、端末で使える制御コマンドの確認
シグナル自体は、kill
コマンドで確認しましょう。
$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
現在、各キーでどんなシグナルを送ったりすることができるかは、stty -a
で確認できます。
$ stty -a
speed 38400 baud; rows 61; columns 220; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; discard = ^O; min = 1; time = 0;
-parenb -parodd -cmspar cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc -ixany -imaxbel iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke -flusho -extproc
例えば、intr
は^C
となっていますが、これがCtrl-C
でありSIGINT
なわけですね。
各項目の意味は、--help
で確認できます。
$ stty --help
シグナルを送るのは、このあたりですね。
$ stty --help | grep signal | grep send
intr CHAR CHAR will send an interrupt signal
quit CHAR CHAR will send a quit signal
susp CHAR CHAR will send a terminal stop signal
[-]hup send a hangup signal when the last process closes the tty
シグナルをトラップする
試しに、こんなスクリプトを書いてみます。
handle_signal.sh
#!/bin/bash
while true; do
echo 'sleeping...'
sleep 1
done
実行権限付与。
$ chmod a+x handle_signal.sh
実行。
$ ./handle_signal.sh
sleeping...
sleeping...
Ctrl-C
で停止。
$ ./handle_signal.sh
sleeping...
sleeping...
sleeping...
sleeping...
^C
ここまでは、ふつうだと思います。
では、ここでtrap
を追加してみましょう。SIGINT
をトラップしてみます。
#!/bin/bash
trap 'echo "trap SIGINT signal"' SIGINT
while true; do
echo 'sleeping...'
sleep 1
done
SIGINT
シグナルを受け取ったら、echo
を実行するようにしてみました。
trap
への引数は、以下のとおりです。
trap 'コマンド' [シグナル名 または シグナルの番号]
今回は、シグナル名で指定しています。
すると、Ctrl-C
で停止しなくなります。
$ ./handle_signal.sh
sleeping...
sleeping...
^Ctrap SIGINT signal
sleeping...
sleeping...
^Ctrap SIGINT signal
sleeping...
sleeping...
これを停止したかったら、別のターミナルからkill
することになります。
$ kill [PID]
こうなってしまうのは、Ctrl-C
で送信するSIGINT
シグナルをハンドリングするようにしたからですね。
ちなみに、SIGTERM
をつけてしまうと、kill
でも停止できなくなります。
trap 'echo "trap SIGTERM signal"' SIGTERM
$ kill [PID]
sleeping...
trap SIGTERM signal
これを停止するには、SIGKILL
シグナルを送ることになります。
$ kill -KILL [PID]
SIGKILL
シグナルは、trap
でハンドリングできません。強制終了シグナルだからです。
Ctrl-D
は?
ところで、Ctrl-D
を入力するとbash
が終了しますね?
stty -a
の結果と
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; discard = ^O; min = 1; time = 0;
--help
を見比べると、どうもシグナルを送るわけではなさそうです。
$ stty --help | grep signal | grep send
intr CHAR CHAR will send an interrupt signal
quit CHAR CHAR will send a quit signal
susp CHAR CHAR will send a terminal stop signal
[-]hup send a hangup signal when the last process closes the tty
これの意味は?
eof = ^D;
「入力終了」を表す文字を送ります、と。
eof CHAR CHAR will send an end of file (terminate the input)
これでbash
が終了するようになっています。
Ctrl-S
を入力するとbash
が停止したように見えたり、Ctrl-Q
で再開できるように見えたりするのも、このあたりの情報から読み解けるので、興味があれば見てみるとよいでしょう。