目录
一,相对于OS的进程状态
1.1运行状态
1.2阻塞状态
1.3挂起状态
二,并发执行与进程切换
2.1,CPU并发执行
2.2进程切换
三,Linux内核管理进程状态的方法
3.1查看进程状态
3.2R状态
3.3S状态
3.4D状态
3.5T状态
3.6X状态
3.7Z状态
3.8孤儿进程
四,进程优先级
4.1优先级理解
4.2查看和调整优先级
4.3如何修改优先级
五,Linux内核调度算法
一,相对于OS的进程状态
什么是相对于OS或者外设的进程状态呢?
就是进程的状态与外设建立联系的,后面我们会提到进程的R,S,T...状态,是为了与这个划分;
1.1运行状态
进程是以运行队列的组织方式来通过调度器进行轮流调度的;调度器会将最前面的进程占用CPU;
占用CPU的进程就是正在运行的程序,而后面排队的是随时准备被调度的进程;这些处于运行队列的进程都处于运行态;
结论:并不是之后占用CPU正在执行的程序才叫运行态,只要是在运行队列中的都交运行态;
1.2阻塞状态
OS管理硬件同样是遵循先描述在组织的原则,所以这些与硬件联系的进程也需要队列来维护,除了运行队列外还需要一个阻塞队列;
为什么需要阻塞队列?
因为外设的速度是很慢的,CPU的运行速度是非常快的,也就是处于运行队列中的进行在一段时间可以执行多次,但是外设可能就反应不过来,所以这个时候进程就需要的等待外设,所以就需要一个阻塞队列在维护等待状态的与外设联系的进程;
1.3挂起状态
挂起状态通常是出现在内存紧张不足的情况下,我们的进程代码和数据,正常情况下下加载到内存中的,但是如果内存资源紧张,OS就会把一些进程的代码和数据再放回到磁盘中,这些置换的进程此时的状态就叫做挂起状态;
出现挂起状态的原因
1.出现挂起状态的原因是内存资源紧张,也就是内存不足,内存里存在着大量的进程,所以一种原因是进程太多,还可能是阻塞进程太多,导致进程没有运行但仍然占用内存;
2.(了解)我们内存中存在一个交换分区,这个分区就是专门处理溢出的资源---硬盘驱动器的一部分,用作交换内存,即RAM的溢出空间;
还需要注意的点:
1.挂起进程并不需要让你知道,就像你玩那个银行存钱,你存进去的钱不知道被用于什么地方;但是只要你需要的时候,银行就会给你,挂起状态的进程,如果准备调度时,OS会把数据和代码块从磁盘上置换回来;
2.挂起状态的进程的PCB可能会处于挂起队列,等待被唤醒或恢复执行;
二,并发执行与进程切换
调度器将进程轮流调度到CPU上运行,这个过程并不是将一个进程运行完毕才放下的,
原因:
1.如果当前进程是死循环,那么将永远不会结束,如果按照执行完才放下的策略,后面的进程将永远无法被调度;
2.为了保证进程的公平性,每个进程都会占用一个时间片,也就是占用CPU一个时间片的时间就会被放下来,让后面的进程运行,这样就保证了处于运行队列中的每一个进程都有机会被CPU运行;
2.1,CPU并发执行
CPU的执行速度是很快的,在一段时间,运行队列的所有进程都会被执行一遍,多个进程轮流被一个CPU调度,这个就叫做CPU并发执行;
2.2进程切换
每个进程在时间片到达之后,会被调度器从CPU上放下去,让后面的进程放到CPU上运行,这个切换的过程就叫做进程切换;
三,Linux内核管理进程状态的方法
先看一下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 */
};
3.1查看进程状态
指令:ps -ajx
3.2R状态
R(running)即运行态:处于运行队列或正在运行的进程;
3.3S状态
S(Sleeping)睡眠状态:这个睡眠是浅度睡眠,也就是可以被唤醒的,通常是等待某一事件的完成;
---->其实就相当于是阻塞状态(与硬件建立联系,等待硬件回应)
我们来验证下:
进程处于R运行态;
我们加上printf试试
我们会发现此时处于S状态,关键就在于此进程与硬件发生交互,可能处于阻塞队列中,也就是可能会处于可中断睡眠状态来等待硬件回应;
3.4D状态
D(Deep Sleep):磁盘休眠状态,也叫做不可中断休眠或不可唤醒状态;这个进程通常是等待IO的完成,但是又等不到IO的完成;
下面 为了方便理解,讲一个故事:
比方说我们现在编译了一段代码,需要将1GB的文件写入磁盘中,内存需要跟磁盘建立联系,磁盘在被写入之前需要判断该行为是否可以被执行,比方说现在磁盘中的空间不足1GB,那么这个请求就应该被驳回,这个过程中我们的内存需要先对磁盘说:“我打算写入1GB的内容,你看看可不可以” 磁盘回复:“那你稍等一下,我看看自己的空间是否足够” 当该信息确认后,然后磁盘回复进程:“我当前空间足够,可以执行” 。然后进程才会通过其对应的代码和数据来将1GB写入磁盘。 所以这个过程有发起也有返回,内存像磁盘申请,磁盘完成后将结果返回给内存,但是这个过程是需要等待的!!
假设当前有大量的进程处于阻塞队列,此时内存不够了,因此操作系统需要杀死一部分进程来保证运行 。当系统压力很大时,依靠内存的辗转腾挪解决不了时,操作系统只能想办法杀死他认为不太重要的进程!!
内存在向磁盘发出请求的时候,在磁盘还没回复是否可行的时候该进程就被操作系统杀死了,所以磁盘想要回复的时候发现该进程不在了,所以就懵圈了。当磁盘想要回应的时候却发现那个等待自己的进程没有了,那么现在写入失败了怎么办?我是应该继续尝试呢,还是丢掉呢??此时不同的操作系统有不同的做法。
比如是在银行,某些数据丢失导致损失了几个亿!!这个时候法官 叫来了 操作系统、进程、磁盘 三个人,来这个过程应该是谁的错,第一嫌疑人是操作系统,因为操作系统杀进程了,操作系统说:“请问我是否履行了自己的职责,我是否是在比较极端的情况下去杀进程的,我能做的最大努力就是保证操作系统不挂掉!!如果我有错,那我下次再遇到这种情况??我还做不做了?就算我不杀进程,导致操作系统挂了,他数据该丢还是会丢,还会影响其他进程,这个责任又该谁负责呢??” 法官觉得操作系统说得有道理,于是又把矛头转向了第二嫌疑人磁盘,因为磁盘在写入失败的时候擅自把数据丢失了。磁盘说:“这不怪我,我就是个跑腿的,我在写入的时候就告诉他可能会失败了,所以我让他在那里等我的结果,可是他人不见了,而是丢失是因为我还有其他工作得做,如果我有错的话,那我们是不是得把磁盘所有逻辑都改了???”法官觉得磁盘说的也有代理,于是又把矛头转向了进程,此时进程扑通一声跪了下来说:“我是被杀的,我怎么能有错呢?”所以凡是存在争议很大的地方,大部分都是因为制度设置的不合理。所以法官说,你们都回去吧,我把操作系统改一改——>让一些进行在磁盘写入完毕期间,这个进程不能被任何人杀掉,其实就是不接受任何响应,但是D状态不多见因为如果有很多说明系统已经临近崩溃了!!
3.5T状态
T(停止状态):OS可以发送信号SIGSTOP信号来停止进程,这个进程是可以通过OS发送信号SIGCONT继续进程的;
暂停和睡眠的区别:暂停是OS发送信号暂时停止进程运行的,而睡眠是进程等待某事件完成;
指令:kill -l
这些都是信号,9是刹进程,18是继续开始进程,19是停止进程;
T状态存在的意义:可能是需要等待某种资源,或者是我们单纯不想让该进程运行!!
应用场景就是gdb,当程序运行起来的时候遇到了我打的一个断点,然后就停下来了,这是这个过程就可以被应用于gdb这个进程在控制被调试的这个进程!
3.6X状态
X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
3.7Z状态
Z(Zombie):僵尸状态,僵尸即人死僵硬无法动弹,进程层面理解就是进程结束了,然后没有后续工作进程(回收资源);
我们来验证一下:
前5秒
后5秒
为什么会存在僵尸状态?
一个进程进行完并不是直接把资源释放,而是要暂时维持状态一段时间-->因为父进程要关心子进程的状态;子进程是父进程创建的,创建他的原因就是让他帮父进程完成某项任务,所以事情完成的如何,进度,最后是要汇报给子进程的;
什么时候会发生僵尸状态?
子进程先父进程结束,而父进程并没有回收子进程的资源,这个时候子进程就会保持僵尸状态等待父进程回收;
如果父进程到了最后也不回收子进程,是不是就会造成泄露?
-->是的,子进程不被回收,那么PCB就会一直存在维护,占用内存,就会造成资源浪费;
3.8孤儿进程
孤儿进程与僵尸进程是反着的,孤儿进程发生的条件是父进程比子进程先结束;
孤儿进程怎么处理呢?
孤儿进程无法被父进程正常回收,就会被系统进程领养,
Ctrl+c无法终止异常进程,底层原理是什么?
ctrl+c的原理是一瞬间父进程被Bash进程回收,所以子进程也就同时在一瞬间被父进程回收掉;
但是如果子进程处于孤儿状态,那么他的PPID就不再是原来的父进程,而是系统进程(PID为1);这时Ctrl+c将不会终止子进程;
四,进程优先级
4.1优先级理解
为什么要有优先级?
因为资源有限,多个进程之间是竞争的,所以先后顺序是肯定存在的,优先级高的先运行,这将大大的改善系统性能;
OS对优先级的调整是什么?
1.IO操作相关调整:在IO完成后会被适当的提高优先级,因为IO操作通常会让进程处于阻塞状态,当IO完成后提高优先级,能让进程优先尽快处理,提高整体效率;
2.时间片相关调整:进程用完时间片后仍未完成,系统可能会降低其优先级,给其他进程更多机会,避免一个进程长时间占用CPU,保证系统的公平性和响应性;
4.2查看和调整优先级
指令:ps -l
与优先级有关是PRI和NI字段;
PRI(priotity)即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小进程的优先级别越高
NI(nice)其表示进程可被执行的优先级的修正数值
PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice
nice其取值范围是-20至19即[-20,19],一共40个级别。
4.3如何修改优先级
优先级=PRI+NI;
PRI是不可修改的都是80但是我们可以修改nice值,
有两种方法:
一,
1.输入指令top
2,按r
3.输入进程的PID
4.填入nice值
二,
使用renice命令
指令:renice (nice值) -p (进程PID)
五,Linux内核调度算法
1.需要维护两个队列按照顺序运行,一个是运行队列,一个是等待队列;时间片到了之后就会转到等待队列;
2.同等优先级的进程遵循先来后到原则;
3.需要维护一个位图,来确定位置
——>因为优先级有各种各样的,比方说在100的位置有2个进程,在133的位置有2个进程,但是我们并不能马上知道这几个地方有进程,而是只能通过遍历数组的方式来一个个查看。最后我们最后当数组遍历到结尾的时候才能确定队列位空
用位图大O(1) 调度算法优化
——>只有100-139一共40个级别,我只需要用5个字节一共40个比特位来标记是否存在进程即可,这样我们就可以通过位运算的方法快速找到 队列中存在进程的位置。 最后当位图位0的时候,就说明队列位空了!!