【Linux】进程状态、优先级和进程切换
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
大家好我是沐曦希💕
文章目录
一、操作系统进程
什么是进程状态我们知道一个程序被加载到内存变成进程之后操作系统要对该进程进行管理即为其创建对应的PCB对象而进程状态本质上就是PCB内部的一个整形变量不同的整形值就对应不同的进程状态。
在普遍的操作系统层面来看进程状态可能有如下几种运行、挂起、阻塞、新建、就绪、等待、挂机、死亡。
进程的不同状态本质都是用来满足不同的运行场景的。
1.运行队列
进程如何在CPU上运行的CPU在内核上维护了一个运行队列进行对进程的管理。让进程入队列本质就是将该进程的task_struct 结构体对象放入运行队列之中。
一个CPU就一个运行队列
2.进程状态
- 运行状态
进程PCB在运行队列里就是运行状态不是说这个进程正在运行才是运行状态。
状态是进程内部的属性所有的属性在PCB里
进程不只是占用CPU资源也有可能随时要外设资源
- 阻塞状态
进程不在运行队列之中进程不能直接被调度而是在等待外设资源的状态进程的PCB就被放在硬件的等待队列中。本质是对tack_struct对象放到不同的队列中
综上所谓的进程不同的状态本质是进程在不同的队列之中等待某种资源
- 挂起状态
如果系统中存在许多进程进程短期内不会被调度代码和数据在短期内不会被执行此时如果内存空间不足操作系统就可以把代码和数据暂时保存到磁盘上节省一部分空间该进程暂时被挂起了这就是挂起状态。
对于阻塞状态和挂起状态阻塞不一定挂起挂起一定是阻塞。
总结进程状态改变的本质是进程对应的 PCB (task_struct 对象) 处于不同设备的运行队列/等待队列中。
二、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 */
};
- R运行状态running : 并不意味着进程一定在运行中它表明进程要么是在运行中要么在运行队列里。
- S睡眠状态sleeping): 意味着进程在等待事件完成这里的睡眠有时候也叫做可中断睡眠interruptible sleep。
- D磁盘休眠状态Disk sleep有时候也叫不可中断睡眠状态uninterruptible sleep在这个状态的进程通常会等待IO的结束。
- T停止状态stopped 可以通过发送 SIGSTOP 信号给进程来停止T进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
- X死亡状态dead这个状态只是一个返回状态你不会在任务列表里看到这个状态。
- z僵尸状态zombie这个状态是一个已经运行完的子进程等待父进程回收它的返回信息。
那么我们可以亲自操作来查看各种状态
ps aux / ps axj //命令
-
运行状态R
-
休眠状态S
这是因为printf要访问外设显示器而外设相对于CPU是比较慢的需要等待显示器就绪要花比较长的时间。 -
停止状态T
而对于S来说是浅度睡眠可以被终止
D是深度睡眠在该状态的进程无法被OS杀掉只能通过断电、进程自己醒来进行解决。通常在要进行多次访问外设或者写数据到外设(高IO的情况下出现。 -
t状态
这也是一种暂停状态tracing stop表示该进程正在被追踪即进行调试时候会显示t状态。(这里不作演示了) -
补充
状态后面带’+‘表示是前台进程状态后面不带’+'是后台进程。
前台进程不能解释命令但是可以用ctrl+c终止该进程
后台进程能解释命令但不可以用ctrl+c终止该进程用kill -9 进程PID来终止进程。
三、两个特殊进程
进程被创建出来是完成任务的进程退出的时候就要知道它完成任务的情况所以该进程不能被立即释放而是对应的资源保存一段上机让父进程或者操作系统来进行读取任务完成情况也有时候我们不关注结果。
1.僵尸进程
进程退出了但退出信息没有父进程或者OS被回收那么此时该进程就处于僵尸进程。
演示
监控脚本命令
defunct的意思是失效的,也就是进程是已经死亡的但是没有被回收。把左侧终止在执行上面监视的命令就不存在上面的进程了这是因为把父子进程都终止的时候操作系统自动回收了。
这就是僵尸进程。内存泄漏不仅仅只体现在malloc\new上在系统中也会存在。
这就是僵尸进程。内存泄漏不仅仅只体现在malloc\new上在系统中也会存在。
- 总结
僵死状态Zombies是一个比较特殊的状态。当进程退出并且父进程使用wait()系统调用,后面讲没有读取到子进程退出的返回代码时就会产生僵死(尸)进程
僵死进程会以终止状态保持在进程表中并且会一直在等待父进程读取退出状态代码。
所以只要子进程退出父进程还在运行但父进程没有读取子进程状态子进程进入Z状态。
- 僵尸进程的危害
进程的退出状态必须被维持下去因为他要告诉关心它的进程父进程你交给我的任务我办的怎么样了。可父进程如果一直不读取那子进程就一直处于Z状态是的
维护退出状态本身就是要用数据维护也属于进程基本信息所以保存在task_struct(PCB)中换句话说 Z状态一直不退出 PCB一直都要维护是的
那一个父进程创建了很多子进程就是不回收是不是就会造成内存资源的浪费是的因为数据结构对象本身就要占用内存想想C中定义一个结构体变量对象是要在内存的某个位置进行开辟空间
内存泄漏
2.孤儿进程
父进程先退出子进程就称之为“孤儿进程”
孤儿进程被1号init进程领养当然要有init进程回收喽。
演示
先来看一看z状态杀掉子进程
重新运行看孤儿进程(杀掉父进程):
父进程结束掉没有看到它的僵尸状态父进程也有父进程都是bash的子进程所以bash这个进程把父进程的资源回收了
此时的2019变成1了1就是对应的操作系统。子进程被1号领养的就是孤儿进程。
父进程先退出的现象是一定存在的子进程会被操作系统领养1号进程这是为了回收子进程退出的时候对应的僵尸管理子进程
被领养的进程就是孤儿进程。
同时子进程以前的状态是S+现在变成了S如果前台进程创建的子进程如果变成孤儿会自动变成后台此时用ctrl+C杀不掉只能用kill解决
四、进程优先级
1.优先级概念
- 优先级PRI
即获取资源的先后顺序和先后被执行的能力。优先级高的先获得和执行优先级低的反之。
存在的原因:资源太少需要资源分配。
cpu资源分配的先后顺序就是指进程的优先权priority。
优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用可以改善系统性能。
还可以把进程运行到指定的CPU上这样一来把不重要的进程安排到某个CPU可以大大改善系统整体性能。
- Linux的优先级
优先级本质就是PCB里面一个整数数字(或者几个整型数字)
Linux优先级有一个特点很快
2.查看系统进程
在linux或者unix系统中用ps –l/ps -al命令则会类似输出以下几个内容
UID : 代表执行者的身份
PID : 代表这个进程的代号
PPID 代表这个进程是由哪个进程发展衍生而来的亦即父进程的代号
PRI 代表这个进程可被执行的优先级其值越小越早被执行
NI 代表这个进程的nice值
3.PRI和NI
Linux中由两个整型数字决定优先级:PRIpriority和NInice
最终优先级 = 老的优先级PRI+ NInice)
注意Linux下的老的优先级PRI默认值是80而NI取值是有范围取值范围是[-20,19]。也就意味着优先级是有取值范围的[80-21,80+19]
在Linux下支持进程在运行中进行优先级调整的调整的策略就是更改nice完成的也就是说会受到nice值影响但是一般情况下不修改
但是大部分情况下nice值是默认的也就是0
这里的PRI优先级是80NI值是0
4.nice值的更改
步骤1sudo top
步骤2进入top后输入r然后在输入进程的pid(进入进程)输入要修改nice的值
用top命令进行修改先输入命令sudo top后在输入r,在输入进程的PID)
看到这个界面就可以开始更改NI值了
- 修改NI值
再输入要修改的值这里以-100为例子出现以下情况最终变成-20
这里在修改成100出现以下情况最终变成19
注意调优先级并不意味着你可以随便调这是操作系统不允许的会导致调度失衡。所以有着一定的取值范围
nice的取值范围是[-20,19]一共40个数字
需要注意的是每次调动优先级时候PID值都会从默认值80开始NI值从0开始不存在累加累减情况。
5.特性
竞争性: 系统进程数目众多而CPU资源只有少量甚至1个所以进程之间是具有竞争属性的。为了高效完成任务更合理竞争相关资源便具有了优先级
独立性: 多进程运行需要独享各种资源多进程运行期间互不干扰
并行: 多个进程在多个CPU下分别同时进行运行这称之为并行
并发: 多个进程在一个CPU下采用进程切换的方式在一段时间之内让多个进程都得以推进称之为并发
这里的独立性对于父进程和子进程是否还是存在yes
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{
pid_t id = fork();
if(id == 0)
{
while(1)
{
printf("this is child process,pid: %d,ppid:%d\n",getpid(),getppid());
sleep(1);
int *p = NULL;
*p = 100;//野指针
}
}
else
{
while(1)
{
printf("this is parent process,pid:%d,ppid:%d\n",getpid(),getppid());
sleep(1);
}
}
}
子进程崩溃并没有影响父进程。
五、进程切换
1.并发
多进程在同一CPU下通过采用进程不断切换的方式让一个单CPU计算机在一个时间段内同时让多个进程代码同时推进的现象称为并发
采用进程切换的方式在一个时间段内不同的进程都可以把代码跑起来同时推进
2.进程如何切换
一个CPU里面存在一套硬件寄存器宏观上寄存器分为用户可见用户不可见
计算机调度某个进程时CPU会把这个进程的PCB地址加载到某个寄存器也就是说CPU内有寄存器可以只找到进程的PCB地址
CPU里有一个eip寄存器PC指针指向当前执行指令的下一条指令的地址。
而进程运行的时候一定会产生很多的临时数据但这些临时数据只属于当前进程虽然CPU内部只有一套寄存器硬件但是寄存器保存的数据只属于当前进程也就是说寄存器硬件不是寄存器内的数据这是两码事寄存器被所有进程共享但是寄存器里的数据时每个进程各自私有的。
- 时间片引出
进程在运行的时候占有CPU但是却不是一直占有到进程结束进程都有自己的时间片因为时间片的存在进程会出现没有被执行完就被拿下去的情况这时候问题来了这个进程下一次如何在次回到CPU继续运行
进程切换的时候需要先进行上下文保护这里的上下文指的是CPU里的寄存器的数据而不是寄存器这里简单理解为临时数据保存至PCB里而当进程恢复运行的时候要进行上下文的恢复该进程在次回到CPU继续运行时重新加载恢复这些数据。