# 管理进程

# 查看进程

# ps

$ ps        # 列出当前终端的进程信息,每行一个进程
    [pid]   # 只显示指定 pid 的进程的信息

    -e      # 等价于 -A ,显示系统的所有进程
    -f      # full ,显示详细信息,包括 PPID
    -o pid,lstart,etime,cmd   # 指定显示的列,其中 lstart 为启动时刻,etime 为运行时长
    -L      # 每行一个线程,增加显示 LWP、NLWP 列
    -T      # 每行一个线程,增加显示 SPID 列。不能与 -L 选项同时使用

    a       # 显示所有终端启动的进程
    f       # 将 command 显示成树状
    r       # 只显示运行中的进程
    u       # 面向 user 的格式(与 top 的显示格式类似)
    x       # 增加显示没有控制终端的进程
    Z       # 增加显示一列安全上下文
  • 安装:yum install procps

  • 例:

    ps auxf                   # 显示所有进程的详细信息,并且将 command 显示成树状
    ps -ef                    # 显示所有进程的详细信息
    
  • 例:

    [root@CentOS ~]# ps auxf
    USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
    root         2  0.0  0.0      0     0 ?        S     2019   0:02 [kthreadd]
    root         3  0.0  0.0      0     0 ?        S     2019   0:40  \_ [ksoftirqd/0]
    root         5  0.0  0.0      0     0 ?        S<    2019   0:00  \_ [kworker/0:0H]
    root         7  0.0  0.0      0     0 ?        S     2019   0:01  \_ [migration/0]
    

    每列的含义:

    • USER :启动进程的用户名。
    • PID :进程的 ID 。
    • %CPU :进程的 CPU 使用率。
    • %MEM :进程的内存使用率。
    • VSZ :Virtual Set Size ,虚拟内存。
    • RSS :Resident Set Size ,常驻内存。
    • TTY :启动进程的终端。
      • TTY 为 ? 表示该进程不是从任何终端启动的。
    • STAT :进程的运行状态。
    • START :进程的启动时刻。
    • TIME :进程占用的 CPU 时长,精确到秒。
      • 注意它不表示进程的运行时长。
    • COMMAND :进程的启动命令。
      • 用方括号包住的 COMMAND 表示内核线程。例如 [kworker/0] 表示运行在 0 号 CPU 上的 kworker 进程。

    一些额外的列:

    • SPID :表示 TID 。
    • LWP :表示 TID 。
    • NLWP :表示该进程中的线程数。

# pstree

$ pstree            # 显示进程树。不过默认将名字相同的进程折叠起来,只计数
        [pid]       # 显示指定进程的所有子进程。默认指定的 PID 为 1
        [user]      # 显示指定用户的所有进程
        -a          # 增加显示每个 command 的命令行参数
        -p          # 增加显示 PID、TID ,这会展开显示每个进程中的线程
  • 安装:yum install psmisc

# pidof

$ pidof <进程名>    # 显示与该名字相同的所有进程的 PID

# 后台进程

  • 执行命令时,
    • 在命令末尾加上 & ,会将该命令移到后台执行,作为后台任务(job)。
    • Ctrl + Z ,会向当前进程发送 SIGSTOP 信号,让它暂停执行,并移到后台任务列表中。
  • 每个后台任务是一个进程组,有一个任务编号 n 。
    • 后台任务不会读取当前终端的 stdin ,但会输出到 stdout 。
    • 不同终端的后台任务列表相互独立。
    • 如果当前终端退出,则后台任务会因为 Process Session 终止而被杀死。

# jobs

$ jobs          # 显示所有后台任务
      -l        # 增加显示 PID
      -p        # 只显示 PID
  • 例:
    [root@CentOS ~]$ nohup ping baidu.com &
    [1] 8646
    nohup: ignoring input and appending output to ‘nohup.out’
    [root@CentOS ~]$ jobs -l
    [1]+  8646 Running                 nohup ping baidu.com &
    

# bg

$ bg <n>        # 启动一个已暂停的后台任务

# fg

$ fg <n>        # 将一个后台任务移到前台运行

# wait

$ wait [n]...   # 阻塞当前终端,直到指定子进程终止
  • 如果指定了 n ,则必须存在 PID 等于 n 的进程,而且它是当前终端的子进程。
  • 如果不指定 n ,则会等待当前终端的所有子进程运行结束。
  • 如果指定了 %n 的格式,则表示某个编号的后台任务,等待其中的进程全部终止。
  • 例:
    [root@CentOS ~]$ wait 1
    -bash: wait: pid 1 is not a child of this shell
    [root@CentOS ~]$ wait %2
    -bash: wait: %2: no such job
    

# 终止进程

# kill

$ kill
       <PID>          # 终止进程(默认是向进程发送 SIGTERM 信号)
       -kill <PID>    # 向进程发送 SIGKILL 信号(这会强制终止进程)
       -9    <PID>    # 向进程发送编号为 9 的信号
       -l             # 列出所有可用的信号

# pkill

$ pkill <pattern>     # 终止所有进程名与 pattern 匹配的进程
        -e            # echo what is killed
        -9            # 向进程发送信号 9
        -t -9 <tty>   # 终止指定终端的所有进程

# 保持进程运行

# nohup

:用于以不挂断(no hang up)的方式执行一条命令。

  • 运行的命令会忽略 SIGHUP 信号,还会忽略 stdin 。
    • 默认将 stdout、stderr 保存到 ./nohup.out 文件中。
  • 用法:
    nohup <command>
    nohup <command> &    # 将进程移到后台运行,以免阻塞前台。此时默认将输出重定向到 nohup.out 文件,相当于执行 nohup <command> &>> nohup.out &
    
  • 下例是一个重启进程的脚本:
    # 该脚本名为 restart.sh ,用于根据启动命令杀死进程,再用 nohup 重启。
    # 使用该脚本之前,需要将 CMD 变量设置为进程的启动命令
    # 使用该脚本时,只需执行 sh restart.sh
    
    CMD="ping localhost"
    
    # 该脚本应该放在进程的工作目录下。每次执行脚本时,先切换到脚本所在目录
    WORK_DIR=$(cd $(dirname $0); pwd)
    cd $WORK_DIR
    
    # 如果有相同进程正在运行,则全部杀死
    PID=`ps auxf | grep "$CMD" | grep -v grep | awk '{print $2}' | xargs`
    if [ "$PID" ]; then
        echo "The process is running, sending SIGTERM to PID: $PID"
        kill $PID
        sleep 5
    
        # 如果进程依然存在,则强制杀死
        PID=`ps $PID | awk 'NR>1 {print $1}' | xargs`
        if [ "$PID" ]; then
            echo "The process is still running, sending SIGKILL to PID: $PID"
            kill -9 $PID
            sleep 1
        fi
    fi
    
    # 重新启动进程
    nohup $CMD &>> $(date +%Y%m%d-%H%M%S).log &
    echo "Started process: $CMD"
    
    也可改为根据 PID 终止进程:
    PID_FILE=$WORK_DIR/pid
    
    if [ -f $PID_FILE ]; then
        kill `cat $PID_FILE`
    fi
    
    nohup $CMD &>> $(date +%Y%m%d-%H%M%S).log &
    echo $! > $PID_FILE
    

# screen

:可以创建多个连接到当前终端的 Session 。即使用户退出了终端,该终端也依然有 Session 存在,不会被系统终止。

已淘汰不用
  • 安装:yum install screen
  • 被 screen 连接的会话记作 attached 状态,没被连接的则记作 detached 状态。
  • 命令:
    $ screen           # 创建一个新会话,并进入其 shell
            -ls        # 列出所有会话
            -dm        # 创建一个脱离的新会话
            -r <pid>   # 连接到一个脱离的会话
            -s <shell> # 指定使用的 shell
            -S <name>  # 指定会话的名字(这会命名为<pid>.<name>,默认的命名为<pid>.<tty>.<host>)
            -L         # 自动将该会话的终端日志记录到/home/screenlog.0 文件中(数字会递增)
    

# tmux

:一个与 screen 类似的工具,但功能更强。

  • 安装:yum install tmux

  • 只需创建一个连接到当前终端的 Session ,然后在该 Session 中创建多个窗口,每个窗口是一个伪终端。

    • 每个窗口还可以拆分成多个面板。
    • 不同用户的 tmux 窗口互不可见。
  • 命令:

    $ tmux                        # 创建一个新窗口,并进入
          new                     # 创建一个新窗口,并进入
            -s <name>             # 设置窗口的名字(不指定的话就使用数字编号)
            -d                    # 在后台创建
          ls                      # 显示所有窗口
          a                       # 进入(attach)上一个窗口
          a -t <name>             # 进入指定的窗口
          kill-session -t <name>  # 终止指定的窗口
          send -t <name> "command" ENTER                      # 向一个窗口输入一条命令,再输入 ENTER
    
  • 进入 tmux 界面之后,

    • 不能在里面创建窗口。
    • tmux 界面的下方会显示一个绿色的任务栏,包含一排窗口名,其中有星号 * 后缀的是当前窗口。
    • 例如,下方的信息表示:目前在名为“4”的会话中,有编号 1~4 四个窗口,都名为 bash 。目前正在使用窗口“3:bash”,上一个窗口是窗口“2:bash”。
      [4] 0:bash  1:bash  2:bash- 3:bash*
      
    • 按 Ctrl+D 会退出当前的窗口或面板。
    • 按 Ctrl+B 会激活控制台,然后便可以输入以下命令:
      d      # 将 tmux 挂到后台(显示 detached)
      s      # 打开会话列表,从而切换到某个会话
      $      # 重命名当前会话
      
      w      # 打开窗口列表,从而切换到某个窗口
      n      # 选择下一个窗口
      p      # 选择上一个窗口
      c      # 创建一个新窗口
      ,      # 重命名当前窗口
      .      # 修改当前窗口的编号(这会重新排序)
      
      [      # 进入阅读模式,可以滚屏、翻页
      "      # 将当前面板拆分成上下两块
      %      # 将当前面板拆分成左右两块
      方向键  # 切换面板
      空格键  # 切换面板布局
      !      # 将当前面板置于新窗口中
      
      ?      # 显示快捷键
      
  • 例:启动 tmux 之后的进程树

    root      3907  0.0  0.1  24248  3696 ?        Ss   Nov08   2:36 tmux
    root      3908  0.0  0.1 116484  3040 pts/1    Ss   Nov08   0:00  \_ -bash
    root     24224  0.0  2.3 282340 43536 pts/1    S+   14:37   0:00  |   \_ ping 127.0.0.1
    root     31780  0.0  0.1 116096  2604 pts/4    Ss+  17:25   0:00  \_ -bash
    root     31854  0.2  0.1 116096  2604 pts/5    Ss+  17:26   0:00  \_ -bash
    root      3057  0.0  0.0 112860  1272 ?        Ss   Sep10   0:00 /usr/sbin/sshd -D
    root     32653  0.0  0.2 156740  5444 ?        Ss   17:44   0:00  \_ sshd: root@pts/3
    root     32656  0.2  0.1 116100  2632 pts/3    Ss   17:44   0:00      \_ -bash
    root     32695  0.0  0.1 155496  1928 pts/3    R+   17:44   0:00          \_ ps auxf
    
    • 可见 tmux 创建了三个伪终端,运行在独立的 Session 中。