前言:在上一篇了解完一部分常见的进程状态后,我们先来把剩下的进程状态了解一下,再来进入进程优先级的学习!
如果对前面Linux进程不太熟悉可以先阅读:
Linux进程
本篇主要内容:
僵尸进程和孤儿进程
Linux进程优先级
1. 僵尸进程
僵尸进程就是处于僵尸状态下的进程!
1.1 什么是僵尸状态
僵尸状态:
- 僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用,后面讲)
- 没有读取到子进程退出的返回代码时就会产生僵死(尸)进程
- 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
- 所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态
僵尸状态就是Linux状态中的X
死亡状态!
1.2 为什么会存在僵尸状态
Linux进程中,当一个进程死亡时不会立刻销毁,而是要等待我们读取死亡信息后才会死亡!
我们创建进程为的就是让他完成某种任务,但是我们该如何知道它是否成功完成,因此在进程退出时,需返回一些退出信息来表明任务得完成情况
比如:
我们之前所学习得
main
都要有return 0
,这也是返回退出信息的一种!
因此我们可以知道:当进程退出但是还没被读取退出信息时处于僵尸状态
PCB释放:
- 当一个进程在退出的时候,退出信息会由OS写入到当前退出进程的PCB中,可以允许进程的代码和数据空间被释放,但是不能允许进程的PCB被立即释放
- 要让OS或者父进程读取到退出进程的PCB中的退出信息,得知子进程的退出原因,才能释放PCB!
综上所述:当一个进程退出了,但是退出信息还没被父进程读取,此时这个退出进程的PCB结构不被放,此时这个退出进程就处于僵尸状态(Z)
1.3 观察僵尸状态
让我们来直观看了解一下僵尸状态
1 #include<stdio.h> 2 #include<unistd.h> 3 #include<stdlib.h> 4 5 int main() 6 { 7 pid_t id = fork(); 8 if(id < 0) return 1; 9 else if(id == 0) 10 { 11 // 子进程 12 int cnt = 5; 13 while(cnt) 14 { 15 printf("i am child, run times: %d\n",cnt--); 16 sleep(1); 17 } 18 printf("i am child ,dead!: %d\n",cnt--); 19 exit(2); 20 } 21 else 22 { 23 // 父进程 24 while(1) 25 { 26 printf("i am father, runing any times\n"); 27 sleep(1); 28 } 29 } 30 return 0; 31 }
观察僵尸状态(Z)
我们可以直观的看到,当子进程退出,父进程没有回收退出信息时,子进程会进入僵尸状态
1.4 僵尸进程的危害
僵尸进程危害
- 进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态!
- 维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护
- 一个父进程创建了很多子进程,就是不回收,是会造成内存资源的浪费,因为数据结构对象本身就要占用内存,是要在内存的某个位置进行开辟空间
- 内存泄漏
关于如何避免僵尸进程带来的危害我们后面细说
2. 孤儿进程
孤儿进程顾名思义就是处于孤儿状态下的进程。
开个玩笑,孤儿进程是父进程在子进程退出之前就先退出了,此时的子进程就称为“孤儿进程”
但是前面刚刚讲过一个进程在死亡时,PCB的死亡信息必须被读取后,才会释放PCB,但是如果父进程已经退出了,子进程的PCB该怎么释放呢?
- 如果不回收,就会占用操作系统的资源
- 因此操作系统会找一个“干爹”为其回收
我们先来写一段代码观察一下
1 #include<stdio.h> 2 #include<unistd.h> 3 #include<stdlib.h> 4 5 int main() 6 { 7 pid_t id = fork(); 8 if(id < 0) return 1; 9 else if(id == 0) 10 { 11 // 子进程 12 while(1) 13 { 14 printf("i am child ......\n"); 15 sleep(1); 16 } 17 } 18 else 19 { 20 // 父进程 21 int cnt = 5; 22 while(cnt) 23 { 24 printf("i am father, run times: %d\n",cnt--); 25 sleep(1); 26 } 27 printf("i am father , dead: %d\n",cnt--); 28 exit(2); 29 } 30 return 0; 31 }
观察孤儿进程
子进程的父进程退出了,子进程要被领养,变成孤儿进程,而通过视频我们发现孤儿进程全部被1号进程统一领养了。
1号进程实际上就是操作系统
3. 进程优先级
3.1 基本概念
基本概念:
- cpu资源分配的先后顺序,就是指进程的优先权(priority)。
- 优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。
- 还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能
优先级的本质就是:得到某种资源的先后顺序
优先级和权限:
- 优先级是能够得到申请的资源,但是需要等待一段时间
- 权限是能不能得到某种资源的使用资格
3.2 查看进程优先级
我们可以用指令查看优先级:
指令:
ps -al
这两个信息就是有关优先级的信息:
- PRI :进程当前优先级,值越小表示优先级越高
- NI :NICE值,表示优先级的修改数据
NICE其取值范围是-20至19,一共40个级别
Linux进程的优先级数值范围:60~99
Linux中默认进程的优先级都是:80
Linux是支持动态优先级调整的
为什么说这两个与优先级有关?
因为:
PRI(新) = PRI(old) + NICE
注意:PRI(old)在修改时,同意按80
处理!
3.3 修改进程优先级
当我们想要修改进程优先级时:
- 输入top,启动任务管理器
- 按r(renice)来修改NICE的值
- 再输入目标进程的pid
- 输入想要修改的NICE值
我们通过视频来直观了解一下:
修改进程优先级
我们发现系统进程的优先级只允许被修改高,而不能往低修改
注意:如果想往低修改需要进入root用户下或者sudo提权。
我们能不能将优先级改为60
以下或者99
以上呢?
NICE取值范围判断
我们通过视频可以看到Linux下的优先级取值范围是60 ~ 99,所以NI的取值范围是 -20 ~ 19,当输入的NI值小于-20时系统会自动将NI变成-20,当输入的NI值大于19时,系统会自动将NI变成19,并不会超出这个范围
那么为什么要设置出这个范围?
- OS 调度的时候,较为均衡的让每一个进程都要得到调度!容易导致优先级较低的进程,长时间得不到CPU资源 --进程饥饿
因此:每一个进程不是占有CPU就一直运行,每隔一段时间,自动被从CPU上剥离下来
Linux 内核支持进程之间进行cpu资源抢占的,基于时间片的轮转式抢占式内核
- 多个进程高频来回的进行切换,逻辑上就是一个CPU划分成了多个CPU只不过性能也会被“分走”,这就是并发
- 并发要研究的是进程间切换,我们下一节再来详谈
4. 总结
本篇文章前部分紧贴上篇Linux进程,分析完了Linux下常见的进程状态,然后初步了解了Linux进程优先级,而进程优先级与前面内容相差较大,希望大家能够多花点时间理解!
谢谢大家支持本篇到这里就结束了