🌏博客主页:PH_modest的博客主页
🚩当前专栏:Linux跬步积累
💌其他专栏:
🔴 每日一题
🟡 C++跬步积累
🟢 C语言跬步积累
🌈座右铭:广积粮,缓称王!
Linux进程状态
基本概念
一个进程从创建而产生到撤销而消亡的整个生命期间,有时占有处理器执行,有时虽可运行但是分不到处理器,有时虽然有空闲处理器但是因为等待某个时间的发生而无法执行,这一切都说明进程和程序不相同,进程是活动的且有状态变化的,于是就有了进程状态这一概念。
进程的状态包括:
/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};
注意点: 进程的当前状态是保存到自己的进程控制块(PCB)当中的,在Linux操作系统当中也就是保存在task_struct当中的。
在Linux操作系统当中我们可以通过 ps aux 或 ps axj 命令查看进程的状态。
[hph@hecs-345360 linux-code]$ ps aux
[hph@hecs-345360 linux-code]$ ps axj
运行状态 - R
一个进程处于运行状态(running),并不意味着进程一定与处于运行当中,运行状态表明一个进程要么在运行中,要么在运行队列中。 也就是说,可以同时存在多个R状态的进程。
进程不是一直运行的,因为可能在等待某种软硬件资源;进程放在了CPU上,也不是一直会运行的,例如时间片,会有一定的轮换时间。
注意点: 所有处于运行状态,即可被调度的进程,都被放到运行队列当中,当操作系统需要切换进程运行时,就直接在运行队列中选取进程运行。
浅度睡眠状态 - S(可中断睡眠)
一个进程处于浅度睡眠状态(sleeping),意味着该进程正在等待某件事情完成,处于浅度睡眠状态的进程随时可以被唤醒,也可以被杀掉。
例如执行以下代码:
代码中调用sleep函数进行休眠100秒,在这期间我们若是查看该进程的状态,则会看到该进程处于浅度睡眠的状态。
[hph@hecs-345360 demo_8_5]$ ps aux | head -1 && ps aux | grep myprocess | grep -v grep
而浅度睡眠的进程是可以被杀死的,可以使用kill命令杀死改进程。
深度睡眠状态 - D(不可中断睡眠)
一个进程处于深度睡眠状态(disk sleep),表示该进程不会被杀掉,即便是操作系统也不行,只有该进程自动唤醒才可以恢复。该状态有时候也叫不可中断睡眠状态(uninterruptible sleep),处于这个状态的进程通常会等待IO的结束。
例如,某一进程要求对磁盘进行写入操作,那么在磁盘进行写入期间,该进程就处于深度睡眠状态,是不会被杀死的,因为该进程需要等待磁盘的回复(是否写入成功)以做出相应的应答。(磁盘休眠状态)。
暂停状态 - T
在Linux中,我们可以通过发送SIGSTOP信号使进程进入暂停状态(stopped),发送SIGCONT信号可以让处于暂停状态的进程继续运行。暂停状态下,是将进程从前台转移至后台。
例如,我们对一个进程发送SIGSTOP(kill -19)信号,该进程就进入到了暂停状态。
我们再对该进程发送SIGCONT(kill -18)信号,该进程就可以继续运行了。
小贴士: 使用kill命令可以列出当前系统所支持的信号集。
[hph@hecs-345360 demo_8_5]$ kill -l
僵尸状态 - Z
当一个进程将要退出的时候,在系统层面,该进程曾经申请的资源并不是立即被释放,而是要暂时存储一段时间,以供操作系统或者是其父进程进行读取,如果退出信息一直未被读取,则相关数据是不会被释放掉的,一个进程若是正在等待其退出信息被读取,那么我们就称该进程处于僵尸状态(zombie)。
首先,僵尸状态的存在是必要的,因为进程被创建的目的就是要完成某项任务,那么当任务完成的时候,调用方是应该知道任务的完成情况的,所以必须存在僵尸状态,使得调用方得知任务的完成情况,以便进行相应的后续操作。
例如,我们写代码时都会在主函数最后return 0。
实际上,这个0就是返回给操作系统的,告诉操作系统代码顺利执行结束。在Linux操作系统当中,我们可以通过使用 echo $?
命令获取最近一次程序退出时的退出码。
[hph@hecs-345360 demo_8_5]$ echo $?
小贴士: 进程退出的信息(例如退出码),是暂时被保存在其进程控制块当中的,在Linux操作系统中也就是保存在该进程的 task_struct 当中。
灵魂三问:
1. 为什么要有Z状态?
答:创建进程是希望这个进程给用户完成工作的,子进程必须得有结果数据,结果数据会保存在PCB中,所以需要等待父进程读取数据。
2. 什么是Z状态?
答:进程已经退出,但是当前进程的状态需要自己维持住,供上层读取,必须出于Z。
3. 如果父进程不读取呢?
答:僵尸状态的进程会一直存在,task_struct 对象也要一直存在,都是要占据内存的,会导致内存泄漏!
死亡状态 - X
死亡状态只是一个返回状态,当一个进程的退出信息被读取后,该进程所申请的资源就会立即被释放,该进程也就不存在了,所以你不会再任务列表当中看到死亡状态(dead)。
其他小知识点
(一)当我们的进程在等待软硬件资源的时候,资源如果没有就绪,我们的进程task_struct只能:
- 将自己设置为阻塞状态(S/D);
- 将自己的PCB连入等待的资源提供的等待队列。
(二)创建一个进程时,先加载PCB,之后再加载对应的代码和数据。
(三)挂起状态
挂起状态的前提条件: 计算机资源已经比较吃紧了!