许吉友 - 运维

对进程的理解

进程是一个独立的程序正在处于运行的状态,它是一个占用CPU、内存、IO资源的实体。

Linux 系统启动后,不会直接管理进程,而是会交给 init 进程管理进程,在一些 Linux 发行版中,比如 CentOS,这个 init 进程就是 systemd。

进程是通过 fork 函数生成的,父进程 fork 出子进程。

如果一个进程的父进程退出了,那么它的父进程就会变成 systemd。

systemd 会管理进程的生命周期,包括处理进程退出。

如果在进程退出时,它的父进程没有管它,它就一直处于僵尸状态,这就是僵尸进程的出现原因。

进程的种类

Linux 系统里有几种不同类型的进程:用户进程(User processes)、守护进程(Deamon processes)和内核进程(Kernel processes)。

进程的元信息

操作系统的角度来说,进程描述为一个结构体---->PCB(在Linux下PCB位task_struct)

task_struct是Linux内核的一种数据结构,他会被装载到RAM(内存)里并且包含着进程的信息:

如何查看进程

$ ls /proc/
$ ps aux
$ ps ef

另外在 top 命令中可以看到各种状态的进程的数量。

进程状态及转换

进程的七大状态:(linux内核里有时候也叫任务)

其中运行状态包括可运行状态

可运行状态

进程获取了所有所需资源,正等待 CPU 时,就会进入可运行状态。处于可运行状态的进程在 ps 的输出中,也已 R 标识。

举例来说,一个正在 I/O 的进程并不立即需要 CPU。当进程完成 I/O 操作后,就会触发一个信号,通知 CPU 和调度器将该进程置于运行队列(由内核维护的可运行进程的列表)。当 CPU 可用时,该进程就会进入正在运行状态。

和正在运行状态一样,进程的状态被设置为 TASK_RUNNING

睡眠状态

当进程所需的资源暂不可用时,就会进入睡眠状态。此时,进程要么主动进入睡眠状态,要么被内核置于睡眠状态(不管你想不想睡,反正内核会让你睡;因此,后者又称为「进程被内核睡了」)。进入睡眠状态的进程,会立即交出 CPU 的使用权。

当进程所需的资源可用时,CPU 会收到一个信号。于是,当调度器下次调度该进程时,会将它置为正在运行或可以运行状态。

以 login shell 进程为例,它

执行 ps -l 可看到与当前 shell 关联的进程,执行 ps -el 则可看到系统上所有进程。如果进程处于睡眠状态,ps 输出中的 WCHAN 字段会显示进程在等待什么系统调用。

$ ps -l | more
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
0 R   867 12085 27779  0  80   0 - 27029 -      pts/480  00:00:00 ps
0 S   867 12086 27779  0  80   0 - 25779 pipe_w pts/480  00:00:00 more
0 S   867 27779 35146  0  80   0 - 27721 do_wai pts/480  00:00:01 bash

例如,在这里,我们执行了 ps -l | more 这个命令。输出中,morebash 都处于睡眠状态。前者是在等待管道输入,即 pipe_wait,因为 ps 输出时,more 还没有接到内容。后者是在 等待 ps -l | more 执行完毕,即等待 do_wait 系统调用。

除了等待资源之外,进程也可以主动进入睡眠状态并持续一段时间。例如,sleep() 函数接收一个时间长度(以秒为单位,比如 10 秒)的参数,然后调用该函数的进程就会进入睡眠状态,并持续 10 秒。当睡眠时间结束后,调度器再次调度到该进程时,会将其设置为可运行状态。之后,当 CPU 空闲时,进程会重新进入正在运行状态。

由此可见,sleep(10) 并不能保证「恰好」睡眠 10 秒,它只保证睡眠时间不少于 10 秒。

部分进程永远不会终止,而是不断地在睡眠、唤醒干活的状态中循环。每次循环开始时,进程进入睡眠状态,然后等待某个特定的事件。当事件发生时,进程被唤醒(进入正在运行或者可以运行状态),然后处理任务。

睡眠状态也分可中断之睡眠状态和不可中断之睡眠状态。

可中断睡眠状态

可中断之睡眠状态表示进程在等待时间片段或者某个特定的事件。一旦事件发生,进程会从可中断之睡眠状态中退出。ps 命令的输出中,可中断之睡眠状态标识为 S

系统会为可中断之睡眠状态的进程设置进程运行状态为:

p->state = TASK_INTERRUPTABLE;

不可中断睡眠状态

不可中断之睡眠状态的进程不会处理任何信号,而仅在其等待的资源可用或超时时退出(前提是设置了超时时间)。

不可中断之睡眠状态通常和设备驱动等待磁盘或网络 I/O 有关。在内核源码 fs/proc/array.c 中,其文字定义为 "D (disk sleep)", /* 2 */。当进程进入不可中断之睡眠状态时,进程不会处理信号,而是将信号都积累起来,等进程唤醒之后再处理。在 Linux 中,ps 命令使用 D 来标识处于不可中断之睡眠状态的进程。

系统会为不可中断之睡眠状态的进程设置进程运行状态为:

p->state = TASK_UNINTERRUPTABLE;

由于处于不可中断之睡眠状态的进程不会处理任何信号,所以 kill -9 也杀不掉它。解决此类进程的办法只有两个: