LoginSignup
39
24

More than 3 years have passed since last update.

bashとCtrlキーで送るシグナル

Last updated at Posted at 2019-09-10

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で再開できるように見えたりするのも、このあたりの情報から読み解けるので、興味があれば見てみるとよいでしょう。

39
24
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
39
24