跳转至

思考题解答

按下 Ctrl + C 后发生了什么

提示

这个问题涉及了键盘按键的处理与信号机制。(答案仅供参考)

解答

当我们在某个终端按下键盘上的 Ctrl + C,键盘发生按下 Ctrl——按下字母 C——字母 C 按键抬起——Ctrl 键抬起四个过程,将四个过程对应的扫描码送到键盘控制器。

键盘控制器向 CPU 发出中断表示键盘有输入需要处理,CPU 调用对应的处理程序(一般是终端驱动)将扫描码翻译为键盘码,对应的字符如果允许,会回显到标准输出(显示器)。

如果是普通字符,则字符存放到对应终端的缓冲区等待读取。

如果像 Ctrl + C 这种特殊字符被检测到,则该处理程序向该终端上的 shell 进程发送 SIGINT,shell 再向其前台进程转发 SIGINT,进程接到该信号,执行对应的信号处理例程。一般情况下,程序将正常退出。

我们可以使用 stty -a 命令列出终端驱动所识别的一些具有特殊含义的字符。注意该命令必须于真正的终端(使用 Ctrl + Alt + Fn 切换得到的终端)才可以使用。

SIGKILL(kill -9)可以杀死所有进程吗?

解答
  1. 状态为 D(Disk sleep)与 Z(Zombie)的进程收到 SIGKILL 信号后会发生什么?

    处于 D 状态的进程正在执行不可中断的系统调用(或者说正在执行无法被中断的内核代码)中,因此发送 SIGKILL 信号之后,需要等待它完成该系统调用,返回内核调度器时才会被杀死。

    处于 Z 状态的进程事实上已经不再执行,所以发送 SIGKILL 信号也不会让它再退出。这种情况一般是它的父进程没有妥善处理好生成的子进程。有时候将其父进程杀死后,子进程会被转交给 init 进程。绝大多数情况下,init 进程都能够恰当处理这个问题,将该进程移除。

  2. 可以向 1 号进程 init 发送 SIGKILL 信号吗?发送之后会发生什么?

    可以发送,但是会被操作系统(内核)忽略,因此 init 不会被 SIGKILL 杀死。

nohup 合适吗?

提示

如果同学的 web 程序崩溃了会发生什么?如果机器重启了呢?

解答

Nohup 是非常方便的工具,但是对于运行需要长时间服务的程序来讲并不可靠。如果出现程序崩溃、系统重启等情况,用 nohup 启动的「服务」不会自动重启。

可以将这个 web 程序配置为 systemd service。如果没有对应服务器的 root 权限,也可以使用 systemd user service,或者使用诸如 supervisor 等工具运行服务程序。

多人使用的 tmux

提示

使用会话(Session)。

解答

在启动 tmux 时,可以为启动的会话命名:

$ tmux new -s session-a  # 启动名为 session-a 的 tmux 会话

脱离 tmux 之后如果还需要再进入这个会话,使用以下命令即可:

$ tmux a -t session-a

对于实验室这个场景,要防止不同用户的操作互相影响,与其他同学提前约定好使用不同的会话名即可。

服务日志

提示

使用 journalctl 查看日志。

解答
  1. 某个服务的日志

    journalctl-u 参数可以指定 unit,例子如下:

    $ sudo journalctl -u ssh  # 查看 ssh 服务的日志
    
  2. 某个正在运行的服务正在输出的日志

    在第一条的基础上,可以指定 -f 参数,以获取正在输出的日志:

    $ sudo journalctl -u ssh -f
    

    按下 Ctrl + C 退出输出即可。

  3. 系统正在输出的日志

    $ sudo journalctl -f
    

    系统日志对于调试系统状态来说是非常有用的信息。

  4. 上一次启动到关机的所有日志

    $ sudo journalctl -b -1
    

    -b 参数表示 boot(启动),-1 表示上一次启动。如果不添加 -1 参数,则默认为当前启动的日志。

耗时的定时任务

解答

Crontab 做的事情就是定时执行任务,它不会去管上一次执行的任务是否已经完成(维护状态也有一定的开销)。设想这样一种情况:

  1. 你使用 crontab 设置了一个每日 rsync 任务(rsync 是用于机器间同步文件的工具),将远程的机器上的一些数据同步到你的机器上;
  2. 有一天,机器突然断网了。此时 crontab 仍然会启动 rsync。由于默认不超时,所以 rsync 会等待网络建立、同步成功后才会退出;
  3. 一个月之后,你发现这台机器断网了,修好之后,你发现同时有 30 个 rsync 进程在执行同步操作,这会对网络以及磁盘都带来很大的压力。

Systemd timer 则会检查任务(timer 对应的服务)是否在执行,如果在执行,就不会产生新的进程。

如果仍然需要使用 crontab,那么可以考虑使用 flock。首先指定一个「锁」文件,然后将命令改成这样:

flock -n [锁文件的路径] [你需要执行的命令]

这样的话,如果其他命令正在执行,那么这个文件锁会被其占用;crontab 尝试再执行命令时,flock 会发现对应的文件已经锁上,因此会立刻退出(-n 参数)。