RHEL8系统管理与性能优化
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

5.1 进程的产生与进程的状态

当产生一个进程时,该进程的所有信息都会被保存在一个叫作进程描述符的结构中。进程描述符中一般存有进程打开的文件、进程的地址空间、进程挂起的信号、进程的状态和PID等信息。内核通过为进程分配PID来标识每个进程。PID是一个数值,其最大值代表系统中可以创建的进程数量的上限,可以通过修改/proc/sys/kernel/pid_max参数来增大此上限。在RHEL8系统中,该值默认为131072。

进程是如何产生的呢?在Linux系统中,通过fork()系统调用产生进程,该系统调用复制父进程来创建一个子进程,每个子进程也有一个PID,由于子进程是复制父进程产生的,因此子进程有和父进程一样的文件、环境变量等资源。子进程执行程序代码,并通过exit()系统调度退出执行。父进程通过wait()系统调用查询退出的子进程的信息,并释放所有资源。图5-1展示了进程的生命周期。

图5-1 进程的生命周期

现在的系统都是多用户和多任务型的。所谓多用户就是允许多个用户登录同一个系统进行各种工作,而多任务指的是允许多个进程并发执行。无论是在单核CPU还是多核CPU中,在某个时间点上总会有一个进程正在执行(R 状态),其他进程会由于各种原因处在不同状态,这些状态信息被保存在文件描述符state域中。常见的状态有以下5种。

(1)TASK_RUNNING:进程正在运行或在运行队列中等待运行,top命令中“S”列代表状态值,其中“R”代表运行状态。

(2)TASK_STOPPED:停止状态,处于此状态的进程是无法投入运行的,除非向进程发送SIGCONT信号恢复运行。例如,使用“ps j|grep firefox”命令查看进程状态,带有字母“T”的代表停止状态(其中的“l”代表多线程)。

(3)TASK_INTERRUPTIBLE:可中断睡眠状态,此状态说明进程处于阻塞中,通常是等待某些条件的到来,一旦条件满足则进入运行状态。系统中的大部分进程都处于睡眠状态,调度程序就是为了实现从这些睡眠状态的进程中挑选合适的进程投入运行的策略。使用ps或top命令可以看到很多进程处于字母“S”表示的状态,这就是睡眠状态。

(4)TASK_UNINTERRUPTIBLE:不可中断睡眠状态,和上一个睡眠状态不同,它不会响应信号,通常是等待某些I/O。例如,某个进程对磁盘发出读写请求时,在等待磁盘的过程中不希望被打断。其实,这类进程是为了保持系统中的数据一致性。通常,这类进程都是短暂的,使用字母“D”来表示这种状态。使用ps命令一般获取不到这类进程。为了演示D状态的进程,这里使用stress-ng压力测试工具模拟发送I/O请求,然后使用top命令获取带有字母“D”的进程。

(5)EXIT_ZOMBIE:僵尸状态,当进程运行完成后,进程的文件描述符仍然保留在内存中,此时进程被设置为僵尸状态,直到父进程通过wait()系统调用查询子进程的退出状态后收回所有资源。但如果父进程未能实现wait(),则子进程运行完成后就一直处于僵尸状态,大量的僵尸进程会消耗PID,这里PID是有限的,因此应该避免出现过多的僵尸进程。为了演示僵尸进程的效果,这里编写一个名为zomble的程序,运行后使用ps命令获取带有字母“Z”的进程,这就是僵尸进程。