1. 前言
限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。
2. 背景
本篇分析基于
Linux 4.14
内核代码进行分析。
3. Ctrl + C 的内幕
有过基于 Linux 内核的操作系统发布版 (如 Ubuntu)使用经验的童鞋都知道,从 Bash 启动一个程序(假定程序包含无限循环,不会退出),然后在控制台输入
Ctrl + C
按键组合,系统会向程序发送一个
SIGINT
信号,在默认设置下,
SIGINT
信号会导致程序终止。当然,可以通过
signal()/sigaction()
配置程序对
SIGINT
信号的处理,使得程序不会退出。
世界上从来没有无缘无故的爱和恨,按下
Ctrl + C
会向程序发送
SIGINT
信号,它背后的逻辑是
TTY 驱动
的支持。在进一步对问题进行讨论之前,先来看一下 Linux TTY 驱动框架图:
app
^
| user space
--------------------|-------------------------------
v kernel space
------------------------
| tty core |
------------------------
^
|
v
-----------------------
| | tty line |
| | discipline |
| ------------|
| |
| tty driver |
| |
-----------------------
^
|
--------------------|-------------------------------
v
hardware
其中:
1. 用户空间
应用层 app 过操作 tty core 提供的字符设备文件(如 /dev/ttyS1),来和 tty 设备交互。
2. tty core
在 TTY 驱动框架中,起到承上启下的作用:用于在用户空间和驱动之间传递数据。
3. tty line discipline
将和 tty driver 交互的数据进行过滤、格式转换。如用于协议数据的处理,如 PPP 和 Bluetooth 。
4. tty driver
tty 硬件设备驱动。
在我们的场景中,正是
tty line discipline
对
Ctrl + C
输入进行了特殊处理。简单的看一下它的处理流程:
/*
* 1. open TTY 设字符设备的过程中,绑定了 tty line discipline 为 N_TTY
*/
tty_open()
tty_open_by_driver(device, inode, filp)
/* 通过字符设备号 @device 找到对应的 TTY 驱动 */
driver = tty_lookup_driver(device, filp, &index);
tty = tty_init_dev(driver, index)
tty = alloc_tty_struct(driver, idx)
/* 设定 tty line discipline 为 N_TTY */
tty_ldisc_init(tty)
struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY);
tty->ldisc = ld;
tty->driver = driver; /* 设定 tty 的驱动 */
tty->ops = driver->ops; /* 设定 tty 的操作接口为驱动接口 */
tty->index = idx;
tty_line_name(driver, idx, tty->name); /* 设定 tty 名称, 如 ttyGS0, ttyS1 等 */
tty->dev = tty_get_device(tty); /* 关联 tty 的设备对象 */
tty_driver_install_tty(driver, tty)
tty->port = driver->ports[idx];
tty_ldisc_setup(tty, tty->link)
/*
* 2. 按下 Ctrl + C 触发 SIGINT 的发送
*/
n_tty_read()
tty_buffer_flush_work(tty->port)
...
n_tty_receive_char_special()
if (L_ISIG(tty)) {
if (c == INTR_CHAR(tty)) {
n_tty_receive_signal_char(tty, SIGINT, c)
isig(signal, tty)
__isig(sig, tty)
kill_pgrp(tty_pgrp, sig, 1) /* 发送 SIGINT 信号 */
}
}
4. 后记
本篇涉及到 Linux TTY 驱动和信号处理的知识,本篇未对它们进行展开,它们都非常复杂,感兴趣的读者可以自行查阅相关资料进行学习。