守护进程

前台、后台、守护进程

  • 前台进程:

    独占命令行窗口,只有其运行完了或者手动终止,才能执行其他命令

  • 后台进程:

    继承当前对话的标准输出和标准错误,而不再读取标准输入,也就是在终端未关闭前仍会向终端打印结果. 后台进程和前台进程的唯一区别就是是否继承标准输入,因此当关闭当前会话 (终端)时,前台进程和后台进程都会终止.

  • 守护进程(deamon, 半人半神的精灵🧚‍♀️):

    与终端完全解绑,即使终端关闭,守护进程也仍会运行. 在排除程序本身被kill的情况下,守护进程的生命周期会持续到系统终止,而前台或后台进程的生命周期是父进程停止或退出,如关闭终端,这也是守护进程名字的来源.

一个小例子

一个每5s向标准输出打印"hello,world!"的小脚本,

1
2
3
4
5
6
#!/bin/bash
while true
do
echo "hello,world!"
sleep 5
done

前台运行时,直接运行该脚本,此时该进程会独占命令行窗口.

让正在运行的前台进程变成后台进程,我们可以先按ctrl+z,将这个进程挂起到后台,此时进程在后台处于暂停状态. 在执行bg命令,让最近一个暂停的后台任务继续执行.

我们可以在运行命令的尾部直接加上符号&,启动的进程就会直接变成后台进程. 此时其与标准输入解绑,我们可以在终端中执行其他命令,该后台进程的标准输出和标准错误还会打印到这个终端中. 当关闭终端时,进程终止.

nohup命令 (no hang up)的作用是不间断地运行某个任务,当直接用nohup命令运行某个进程时,程序的默认输出会重定向到nohup.out文件中. 当关闭终端时,这个进程也还会运行,但此时并没有实现终端的标准输入和进程的解绑,按ctrl+c还会终止该进程,我们也无法在终端中继续执行其他操作.

因此一般情况下,我们把nohup&一起使用,把进程变成后台的与终端解绑的守护进程.

更深入的认识

进程组

每个进程都属于某一个进程组,进程组的目的是简化向组内所有进程发送信号的操作. 即如果一个信号是发给一个进程组,则进程组内的所有进程都会收到该信号. 进程组内的所有进程都有相同的PGID,进程组的PGID等于进程组组长的PID (Process Identification Number),进程组组长也被称为进程组先导 (Process group leader),同一进程组中除了进程组先导外的其他进程都是其子进程.

会话

很多时候会话 (session)和终端 (terminal)的概念是差不多的,一个会话最多有一个终端,也可以没有,该终端由会话中所有的进程组中的进程所共用. 会话是一个若干进程组的集合,同样,系统中的每一个进程组也都必须属于某一个会话. 一个会话中的前台进程组只会有一个,只有其中的进程才可以和控制终端进行交互. 除了前台进程组外的进程组,都是后台进程组. 会话中也有会话先导 (session leader)的概念,用来表示建立到控制终端连接的进程. 在拥有控制终端的会话中,会话先导也被称为控制进程,即登入系统的shell进程 (login shell).

可以做一个小实验来帮助理解这些概念,分别用命令行和脚本的方式运行sleep 50 &,后台睡眠50s,执行ps -l来查看当前进程的详细信息. ps是Process Status的缩写.

每一列都涉及很多Linux系统的知识...这里先简单学习一下.

其中S列表示这个进程的状态,S表示这个进程正在睡觉,R表示这个进程正在运行. UID为USER ID,即拥有整个进程的USER的ID,不同用户的UID储存在/etc/passwd文件中,下面是这个文件的一部分,

以冒号为分割,每一行中的各项分别代表

1
用户名: 密码 (x表示加密密码): UID (用户标识): GID (用户组标识): 用户的全名: 用户的家目录: 登录后使用的shell

C的含义是CPU,即进程占据的资源百分比.

PRI (Priority)和NI (Nice)共同决定了进程的优先级,进程的总优先度值为 PRI + NI,这个数值越小 (越靠前),则进程的优先级别越高.

ADDR是Address的缩写,表示进程的内存地址.

SZ为Size的缩写,表示进程占用的内存大小.

WCHAN是Waiting Channel的缩写,表示内核中进程睡眠处的内核函数名称,根据原因不同不同进程可能睡在内核中不同的地方,如果这个进程正在运行中 (R),那么此字段值为'-'.

TTY代表teletype,终端.

TIME为进程的CPU运行时间,当进程正在等待网络或磁盘的IO,或只是休眠时,TIME不会增加.

CMD (Command),代表进程所执行的命令.

上面得到的结果中,第一行是终端中的运行的bash,第二行是直接在终端中输入的sleep 50 &,因为它是由终端中的bash执行的,因此它的PPID和第一行bash的PID是一样的. 第四行是执行的ps -l命令,也是由终端中的bash执行,因此PPID也是当前bash的PID. 第三行是通过脚本运行的./sleep.sh,这句话另起了一个bash来执行该脚本,因此并不是终端中bash的子进程,PPID不同.

SIGHUP信号

SIGHUP信号是终端挂断的意思,当我们通过控制终端连接到一个bash,启动了一些进程后,断开这个终端时,所有启动在终端中的进程,包括前台进程组和后台进程组,都会收到SIGHUP信号,此时全部进程都会退出. 而由于守护进程已经和终端分离了,因此SIGHUP信号对它没有作用,相当于废物. 但是我们可以废物利用一下,因此很多守护进程设置为一旦监听到了终端的SIGHUP信号,就重新读取配置文件.