^C をインターセプトし、接続されたプロセス (シェル) に送信されるシグナルに変換するのは端末 (ドライバー) です。 stty intr ^B
代わりに ^B をインターセプトするようにターミナル ドライバに指示します。 ^C を端末に返すのも端末ドライバーです。
シェルは、回線の反対側に位置する単なるプロセスであり、端末ドライバー (/dev/ttyX など) を介して端末から stdin を受信し、stdout (および stderr) も同じ tty に接続されます。 .
(エコーが有効になっている場合) 端末はキーストロークを 両方 に送信することに注意してください。 プロセス (グループ) を終了し、端末に戻ります。 stty コマンドは、tty を「制御する」プロセスの tty ドライバー用の ioctl() の単なるラッパーです。
更新:シェルが関与していないことを示すために、次の小さなプログラムを作成しました。 exec ./a.out
を介して親シェルで実行する必要があります (とにかく、インタラクティブ シェルはドーター シェルをフォークするようです) プログラムは、SIGINTR を生成するキーを ^B に設定し、エコーをオフに切り替え、標準入力からの入力を待ちます。
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
int thesignum = 0;
void handler(int signum);
void handler(int signum)
{ thesignum = signum;}
#define THE_KEY 2 /* ^B */
int main(void)
{
int rc;
struct termios mytermios;
rc = tcgetattr(0 , &mytermios);
printf("tcgetattr=%d\n", rc );
mytermios.c_cc[VINTR] = THE_KEY; /* set intr to ^B */
mytermios.c_lflag &= ~ECHO ; /* Dont echo */
rc = tcsetattr(0 , TCSANOW, &mytermios);
printf("tcsetattr(intr,%d) =%d\n", THE_KEY, rc );
printf("Setting handler()\n" );
signal(SIGINT, handler);
printf("entering pause()\n... type something followed by ^%c\n", '@'+THE_KEY );
rc = pause();
printf("Rc=%d: %d(%s), signum=%d\n", rc, errno , strerror(errno), thesignum );
// mytermios.c_cc[VINTR] = 3; /* reset intr to ^C */
mytermios.c_lflag |= ECHO ; /* Do echo */
rc = tcsetattr(0 , TCSANOW, &mytermios);
printf("tcsetattr(intr,%d) =%d\n", THE_KEY, rc );
return 0;
}
intr.sh:
#!/bin/sh
echo $$
exec ./a.out
echo I am back.
シェルは入力したものすべてをエコーするので、 ^C
と入力すると 、それもエコーされます(そして、あなたの場合はシグナルハンドラによって傍受されます)。コマンド stty -echo
ニーズ/制約によっては、役立つ場合とそうでない場合があります。詳細については、stty の man ページを参照してください。
もちろん、いつでも、より低いレベルでさらに多くのことが行われます 周辺機器デバイス ドライバー (^C 信号の生成に使用するキーボード ドライバーや、すべてを表示するターミナル ドライバーなど) を介してシステムと通信します。アセンブリ/機械語、レジスタ、ルックアップ テーブルなどのレベルでさらに深く掘り下げることができます。より詳細で深いレベルの理解が必要な場合は、以下の本から始めるのが良いでしょう:
The Design of the Unix OS は、この種のものの良い参考資料です。さらに 2 つの古典的なリファレンス:Unix プログラミング環境と UNIX 環境での高度なプログラミング
この SO の質問のすばらしい要約は、どのように Ctrl-C で子プロセスを終了させますか?
"たとえば find
のようなプログラムを実行するとき 、シェル:
- シェルフォークそのもの
- 子に対してデフォルトのシグナル処理を設定
- 子を指定されたコマンド (例:find) に置き換えます
- CTRL-C を押すと、親シェルはこのシグナルを処理しますが、子シェルはそれを受け取り、デフォルトのアクションで終了します。 (子もシグナル処理を実装できます)"