Mit:the missing semester 5

课程目标

学习如何同时执行多个不同进程并追踪他们的状态,如何停止或暂停某个进程以及如何使进程在后台中运行。学习改善shell及其他工具的工作流的方法。学习如何使用SSH操作远端机器。

任务控制

某些情况我们需要中断正在执行的任务,比如当一个任务需要执行很长时间时,我们可以使用Ctrl-C来停止命令的执行。

  • 结束进程

    shell会使用UNIX提供的信号机制执行进程间通信。当一个进程接收到信号时,就会停止执行,处理该信号并基于该信号传递的信息来改变其执行,即信号是一种软件中断

    如上面的例子,当我们输入Ctrl-C时,shell会发送一个SiGINT信号到进程。但程序可以忽略它,例如下面这段程序:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #!/usr/bin/env python
    import signal,time
    def handler(signum,time)
    print("\nI got a SIGINT,but I am not stopping")
    signal.signal(signal.SIGINT,handler)
    i=0
    while True:
    time.sleep(1)
    print("\r{}".format(i),end='')
    i+=1

    如果向这段程序发送SIGINT,程序并不会结束,只有向程序发生SIGQUITCtrl-\,程序才会退出

    尽管SIGINTSIGQUIT都常用于发送终止程序相关的请求,SIGTERM是跟家通用、也更加优雅的退出信号。为了发送这个信号,我们需要使用kill命令,kill -TERM <PID>

  • 暂停和后台执行进程

    信号可以让进程做其他的事情,而不仅仅是终止他们。例如SIGSTOP会让进程暂停,在终端中,Ctrl-Z会让shell发送SIGTSTP信号(SIGSTOP不能被忽略,SIGTSTP可以被忽略),可以使用fgbg命令恢复暂停的工作,分别表示在前台继续或在后台继续。

    jobs命令会列出当前终端会话中尚未完成的全部任务,可以使用pid引用这些任务(可以用pgrep找出pid),也可以使用百分号+任务编号选取任务。如果要选择最近的一个任务,可以使用特殊参数$!

    Tips:命令中的&后缀可以让命令直接在后台运行,可以直接在shell中进行其他操作,但是进程此时还是会使用shell的标准输出(可以使用shell重定向处理解决)

    键入Ctrl-Z可以让运行的进程转到后台运行,输入bg让进程在后台继续。但此时后台的进程仍然是终端进程的子进程,一旦关闭终端(会发送信号SIGHUP),后台的进程也将终止,为了防止此情况发生,可以在命令前加nohup (用于忽略SIGHUIP的封装)来运行程序。

    SIGKILL是一个特殊的信号,不能被进程捕获,并且它会马上结束该进程,不过会有些副作用,例如留下孤儿进程(父进程退出,但子进程还在运行)

终端多路复用

在使用命令行时,通常会希望同时执行多个任务,此时可以使用终端多路复用器。

像tmux,screen这类终端多路复用器可以允许我们基于面板和标签分割多个终端窗口,可以同时与多个shell会话进行交互。并且可以分离当前终端会话并在将来重新连接,大大改变了操作远端设备的工作流,避免了nohup和其他类似技巧的使用。

tmux是现在最流行的终端多路器。可以使用相关快捷键创建多个标签页并在它们间导航。tmux的快捷键都是类似<C-b> x这样的组合,需要先按下Ctrl-B,松开后按下X

  • 会话:每个会话都是独立的工作区,包含一个或多个窗口
    • tmux 开启一个新的会话
    • tmux new -s Name 以指定名称开启一个新的会话
    • tmux ls 列出当前所有会话
    • tmux a 重新连接最后一个会话
  • 窗口:相当于浏览器中的标签页,将会话分割为多个部分
    • <C-b> c 创建一个新的窗口
    • <C-b> N 跳转到第N个窗口
    • <C-b> p 跳转到前一个窗口
    • <C-b> n 跳转到下一个窗口
    • <C-b> , 重命名当前窗口
    • <C-b> w 列出当前所有窗口
  • 面板:像Vim中的分屏一样,可以在一个屏幕显示多个shell
    • <C-b> " 水平分割为两个面板
    • <C-b> % 垂直分割为两个面板
    • <C-b> 方向 切换到指定方向的面板(方向指键盘的方向键)
    • <C-b> z 切换当前面板的缩放
    • <C-b> 空格 在不同的面板排布间切换

别名

输入一长串指令非常麻烦,大部分shell都支持设置别名,相当于一个长命令的缩写,shell会自动将其替换为原本的命令。例如:

1
alias alias_name="command_to_alias arg1 arg2"

注意=两边没有空格,因为alias是一个shell命令,只接受一个参数。

alias常用特性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 创建常用命令的缩写
alias ll="ls -lh"

# 能够少输入很多
alias gs="git status"
alias gc="git commit"
alias v="vim"

# 手误打错命令也没关系
alias sl=ls

# 重新定义一些命令行的默认行为
alias mv="mv -i" # -i prompts before overwrite
alias mkdir="mkdir -p" # -p make parent dirs as needed
alias df="df -h" # -h prints human readable format

# 别名可以组合使用
alias la="ls -A"
alias lla="la -l"

# 在忽略某个别名
\ls
# 或者禁用别名
unalias la

# 获取别名的定义
alias ll
# 会打印 ll='ls -lh'

PS:默认情况下,shell并不会保存别名,为了让别名持续生效,需要将配置放进shell的启动文件里,例如.bashrc.zshrc

远端设备

教程详细说明的SSH的各种高级用法,但我认为部分用法用处不大,且有更方便的方法,故此处只写SSH的基本操作。

  • 连接:

    通过如下命令,可以使用ssh连接到其他服务器

    1
    2
    ssh foo@bar.mit.edu
    #尝试以用户名foo 登录服务器bar.mit.edu
  • SSH密钥

    只需要向服务器证明客户端持有对应的私钥,无需公开私钥,可以避免每次都输入密码。私钥(通常是~/.ssh/id_rsa或者~/.ssh/id_ed25519)等效于密码,好好保管。

  • 密钥生成

    1
    ssh-keygen -o -a100 -t ed25519 -f ~/.ssh/id_ed25519