文章目录
- 一. OS进程状态
- 1.1 一般分类
- 1.2 运行状态
- 1.3 阻塞状态
- 1.4 挂起状态
- 1.5 并行与并发
- 二. Linux进程状态
- 2.1 分类
- 2.1.1 R状态
- 2.1.2 S状态
- 2.1.3 D状态
- 2.1.4 T状态与t状态
- 2.1.6 X状态
- 2.2 僵尸状态(Z)
- 2.2.1 概念
- 2.2.2 查看状态
- 三. 进程的优先级
- 3.1 概念
- 3.2 查看系统进程
- 3.2.1 PRI and NI
- 3.2.2 top指令可以改变nice值
一. OS进程状态
1.1 一般分类
在操作系统中,统一的一般会存在这几种状态:创建,就绪,阻塞,执行,终止。
😇简单讲讲,进程在刚开始处于创建状态,随后进入就绪状态,等待系统调用,调用后进入执行状态。在这里会进入一个岔口,如果是scanf等需要用户做出行为推进的进程,进程会进入阻塞状态等待用户输入;如果进程所用的时间片用完了,进程会回到就绪状态;如果进程结束(进程的任务结束了),就会进入终止状态。
👇OS进程中有三种状态值得一说:
1.2 运行状态
对于分时操作系统来说,OS需要一个调度器来保证CPU的资源被合理的调用,所以OS需要在内存维护一个运行队列,他将进程的task_struct结构体连接起来,此时,处于这个队列的进程就处于运行状态,这说明运行状态并不指的是正常运行的进程,而是处于运行队列中!处于运行队列中,说明该进程可以随时被CPU调度!!
1.3 阻塞状态
OS管理硬件的方式也是先描述再组织,先描述出硬件统一的数据结构,再以链表的形式将他们管理起来起来,在硬件的结构体中,还会维护一个阻塞队列,当硬件没有准备好数据的时候,该进程只能在阻塞队列中等待!!
——》所谓的阻塞状态,和运行状态一样,本质都是处于不同的队列中!!
——》当用户执行程序到scanf时候,如果用户一直不输出,那么进程就会一直处于阻塞状态!!
1.4 挂起状态
当OS内存资源严重不足时,OS会将阻塞队列中一些阻塞进程的数据与代码,从内存中搬出到磁盘中来节省空间,处于这个状态的进程就处于挂起状态。
-
导致OS内存资源严重不足的一种情况是存在大量处在阻塞队列的进程,所以我们要办法将一些资源置换到磁盘中,但是为了不影响阻塞队列的管理,所以大多数情况下并不是直接将task_struct结构体置换过去,而是将该进程的数据和代码先置换过去,而当执行到该进程的时候,再通过某种方式将其数据和代码置换回来。
-
磁盘中会有一个专门的分区swap来存储处于挂起状态的进程的代码与数据。
-
挂起状态是一种特殊的阻塞状态,他依然在阻塞队列中,只不过他的代码和数据都在磁盘中。
-
处于挂起状态的进程是不会告诉用户的!
-
代码和数据在磁盘中,这意味着每次需要调度的时候,就需要频繁的换入与换出,我们从冯诺依曼体系中可以知道,外设和内存的交互是要走IO线的,比起在内存中交互是很慢的!!因此,挂起状态这样解决内存空间不足的方法,是一个空间换时间的方法!!
1.5 并行与并发
调度器将进程放到CPU上去运行,并不代表必须要将进程全部运行完才会被放下来!!这是因为,在分时操作系统中,调度器要保证CPU资源被合理使用,不能让一个进程占用CPU太长时间。
——>那么什么时候应该把这个进程放下来,就要取决于时间片。当一个进程在超过时间片还没有结束的时候,就要把他放下去然后重新放在新的运行队列的位置。
CPU运行速度是很快的,所以这个过程我们感受不到, 而大量地把进程从CPU上拿上来在放下去的这个过程,称之为进程切换!多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,这个过程称之为 并发执行 ;多个进程在多个CPU下,分别同时进行运行,这称之为 并行执行!
——>并行与并发的区别在于,一个是单CPU,一个是多CPU。
二. Linux进程状态
上面所述的,是一般操作系统的进程中,统一的几种状态,在Linux操作系统中,当然对进程的状态有单独的设计,下面的状态在kernel源代码里定义:
/*
* 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 */
};
所有进程查看指令:
ps aux / ps axj
2.1 分类
2.1.1 R状态
R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列
里。
这里写一个while循环能直观的看到这个状态:
2.1.2 S状态
S睡眠状态(sleeping): 意味着进程在等待事件完成。也是一种阻塞等待状态。这里的睡眠有时候也叫做可中断睡眠状态。
我们再上面的代码稍作修改,在while循环里执行printf,就能观察到这个现象:
接下来会引申出几个问题
❓Q1:为什么上一段代码是R状态,而在循环中加了printf就是S状态呢??
👉 因为printf需要一直和显示器建立联系,CPU计算的速度很快,相比之下,数据从走IO线再到显示屏打印数据的速度是很慢的,所以进程有很大概率一直处在等待状态,因为需要等显示器准备好(CPU太快了 所以显示器缓不过来)
❓Q2:键盘会因为用户不输入而卡着,那为什么显示器也会卡着呢??
👉 因为cpu的速度比外设快太多了,所以大多数的进程都在等待,另一方面我们当前的机器是云服务器,用xshell进行链接,从硬件角度上,不仅会走IO线,还要走网卡,速度是很慢的。
我们的bash进程同理,如果我们一直不输入指令的话,他也会卡在那里。
2.1.3 D状态
D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的
进程通常会等待IO的结束。
与S状态不同,S状态(浅度睡眠)是可以被唤醒的,而D状态(深度睡眠)是不可被唤醒的,唤醒的意思就是不能中途杀掉这个进程。
——》处于D状态的进程负责的任务都是磁盘级的数据运输。在进行磁盘级的大内存数据搬运时,该进程会等待数据搬运的IO结束,并且记录运行的结果(数据搬运失败,磁盘空间不足或者搬运成功),如果将这样的进程杀掉,如果数据搬运失败,数据就有丢失的风险。OS可能会在内存空间不足时,会杀掉部分进程,而D状态的进程不会被优先杀掉。
2.1.4 T状态与t状态
T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可
以通过发送 SIGCONT 信号让进程继续运行。
指令:kill -l
处于T状态的进程,可能是进程在运行中被用户强制终止,也可能是进程做了非法而不致命的操作,被OS终止了。
另外,处于gdb(调试)状态的进程,是t状态进程。
2.1.6 X状态
X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
2.2 僵尸状态(Z)
2.2.1 概念
僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程没有读取到子进程退出的返回代码时就会产生僵死(尸)进程,僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态。
——》打个比方,如果一个人如果突然死亡,他的身体会先通过法医检测死亡原因,再将其交给家属,并告知死亡原因。回到进程的角度,死亡的人是子进程,他的身体通过法医检测的状态就是僵尸状态,将其身体交回给家属就是向父进程返回子进程信息!子进程信息返回后才会死亡!!
——》由此可见,一个进程在退出的时候并不是立即把所有的资源全部释放,而是要把当前进程的退出信息维持一段时间!!——>因为父进程需要关心子进程!!
2.2.2 查看状态
为了观察到这个现象,我们需要让子进程中途退出,用到exit函数:
——》这里需要说明,像此类退出程序的函数是没有返回值的,因为执行了这个函数,该进程就终止了,该进程本身就取不到返回值,设置返回值也就没有意义。
- 💬情况1,父进程存在,子进程中断。
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
pid_t id = fork();
if (id == 0)
{
int cnt = 5;
while (cnt)
{
printf("child,pid:%d,ppid:%d,cnt:%d\n", getpid(), getppid(), cnt);
--cnt;
sleep(1);
}
exit(0);
}
else
{
while (1)
{
printf("parent,pid:%d,ppid:%d\n", getpid(), getppid());
sleep(1);
}
}
return 0;
}
子进程退出,只要父进程不退出,没有处理子进程的返回信息,子进程就会一直处于Z状态,而存储子进程状态的PCB也就一直不会释放,就会一直占用内存!!
疑问1:为什么要有僵尸状态?
💡子进程需要处于僵尸状态,维护自己的task_struct,方便未来父进程读取退出状态。
疑问2:如果创建了很多的僵尸状态的子进程,不处理这些数据,会一直消耗内存(甚至内存泄漏吗)?
💡是的!!如果Z状态一直不退出,维护返回信息的PCB就一直不会释放,也就会一直消耗内存!!
💬情况2,父进程中断,子进程存在。
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
pid_t id = fork();
if (id != 0)
{
int cnt = 5;
while (cnt)
{
printf("parent,pid:%d,ppid:%d,cnt:%d\n", getpid(), getppid(), cnt);
--cnt;
sleep(1);
}
exit(0);
}
else
{
while (1)
{
printf("children,pid:%d,ppid:%d\n", getpid(), getppid());
sleep(1);
}
}
return 0;
}
当父进程中断,子进程存在时,子进程会被系统进程“收留”,也就是其父进程会改为系统进程,这样的子进程也被称为孤儿进程!
疑问😕1:为什么会被系统进程收留?
💡父进程被终止,子进程不能没有进程管理,因为被管理,子进程的相关资源才能被回收,此时就会将子进程交给系统进程领养。
疑问😕2:在上面的实验中,我们可以观察到一个现象,子进程仍在运行,但是Ctrl C却终止不了它了,这是为什么?
💡这是因为,子进程被系统进程领养后,ppid被改为了1,它与bash进程(命令行进程)就不是直接的亲属关系了,我们的Ctrl C命令实际上也是对bash进程下达命令,因为bash管不了子进程了,Ctrl C也就终止不了它了。
疑问😕3:子进程是bash进程的孙子进程,为什么父进程消亡后不由bash进程回收而是交由系统进程回收?
💡因为bash做不到,因为孙子进程不是他去创建的! 他没有这个权限,而系统进程可以做到,因为要将孤儿进程托孤给系统进程。当然不同的操作系统具体的实现方法可能也不同!!
三. 进程的优先级
3.1 概念
1. 😕什么是进程优先级??
——》cpu资源分配的先后顺序,就是指进程的优先权(priority)
2. 😕权限 VS 优先级,它们的区别?
——》权限是我“能不能做”,而优先级是我“能不能先做”,它们对待的不是一种问题,优先级越高的进程,越优先被系统调用。
3. 😕分时操作系统不是保证进程公平调用吗,为什么要优先级?
——》因为绝大多数的时候,CPU资源是少数,而进程有多数,狼多肉少,进程与进程之间相互竞争,优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。
3.2 查看系统进程
在linux或者unix系统中,用ps –l命令可以查看系统进程:
从上面的显示信息中,我们很容易注意到其中的几个重要信息,有下:
- UID : 代表执行者的身份——》记录进程是谁启动的。
- PID : 代表这个进程的代号
- PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
- PRI :代表这个进程可被执行的优先级,其值越小越早被执行
- NI :代表这个进程的nice值
——》为什么会有UID?在Linux下,一切都是文件,是文件就有自己的文件属性,有自己的拥有者,所属组对应的权限。进程也是一种文件的体现!!我们所有的操作,都是进程执行的,进程也会记录自己的拥有者,所属组,以及它们所对应的权限!
3.2.1 PRI and NI
PRI也还是比较好理解的,即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小
进程的优先级别越高
那NI呢?就是我们所要说的nice值了,其表示进程可被执行的优先级的修正数值。
PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice。
这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行,所以,调整进程优先级,在Linux下,就是调整进程nice值。
PRI初始值为80,nice值的取值范围为[-20,,19],这样的话,权限一共有40个等级。
3.2.2 top指令可以改变nice值
进入top后按“r”–>输入进程PID–>输入nice值
需要强调一点的是,进程的nice值不是进程的优先级,他们不是一个概念,但是进程nice值会影响到进
程的优先级变化。
可以理解nice值是进程优先级的修正修正数据。
本文就到这里,感谢你看到这里❤️❤️! 我知道一些人看文章喜欢静静看,不评论🤔,但是他会点赞😍,这样的人,帅气低调有内涵😎,美丽大方很优雅😊,明人不说暗话,要你手上的一个点赞😘!
希望你能从我的文章学到一点点的东西❤️❤️