为什么空循环进程会导致CPU占有率很高?

最近在写一个时间戳系统记录系统中各个模块一些重要的时间节点。获取内核上报的时间戳是通过进程轮询的方式,但是发现该进程在实际运行中CPU占用率很高,其中就是一个死循环在读取设备文件,本以为是由于读系统调用太频繁的原因。
于是又写一个测试程序,其中仅仅是个空循环。结果发现该程序的CPU占用率居然在90%多!
针对这个问题我有2个疑问,对这些疑问的理解也记录在此,与大家分享。

1 linux系统是时间片调度算法,微观上所有可运行进程都是串行,不管进程中作何操作,该进程的时间片一到就切换到下一进程,那为什么一个空循环进程CPU占用率还这么高。

要彻底搞明白这个问题,需要弄清楚2个问题
(1)linux进程的几种状态以及其切换关系。
(2)CPU占用率如何计算出来的。

linux进程的几种状态以及其切换关系在《深入学习linux内核》中有详细介绍,这里简单说下。
进程是一个动态的实体,所以他是有生命的。从创建到消亡,是一个进程的整个生命周期。在这个周期中,进程可能会经历各种不同的状态。一般来说,所有进程都要经历以下的3个状态:

就绪态。指进程已经获得所有所需的其他资源,正在申请处理处理器资源,准备开始执行。这种情况下,称进程处于就绪态。

阻塞态。指进程因为需要等待所需资源而放弃处理器(如系统调用阻塞或者sleep),或者进程本不拥有处理器,且其他资源也没有满足,从而即使得到处理器也不能开始运行。这种情况下,进程处于阻塞态。阻塞状态也称休眠状态或者等待状态。

运行态。进程得到了处理器,并不需要等待其他任何资源,正在执行的状态,称之为运行态。只有在运行态时,进程才可以使用所申请到的资源。在所有进程中同一时刻仅有一个进程处于运行态。

在Linux系统中,将各种状态进行了重新组织,由此得到了Linux进程的几个状态:
RUNNING:正在运行或者在就绪队列中等待运行的进程。也就是上面提到的运行态和就绪态进程的综合。一个进程处于RUNNING状态,并不代表他一定在被执行。由于在多任务系统中,各个就绪进程需要并发执行,所以在某个特定时刻,这些处于RUNNING状态的进程之中,只有一个能得到处理器,而其他进程必须在一个就绪队列中等待。即使是在多处理器的系统中,Linux也只能同时让一个处理器执行任务。
UNINTERRUPTABLE:不可中断阻塞状态。处于这种状态的进程正在等待队列中,当资源有效时,可由操作系统进行唤醒,否则,将一直处于等待状态。
INTERRUPTABLE:可中断阻塞状态。与不可中断阻塞状态一样,处于这种状态的进程在等待队列中,当资源有效时,可以有操作系统进行唤醒。与不可中断阻塞状态有所区别的是,处于此状态中的进程亦可被其他进程的信号唤醒。
STOPPED:挂起状态。进程被暂停,需要通过其它进程的信号才能被唤醒。导致这种状态的原因有两种。其一是受到相关信号(SIGSTOP,SIGSTP,SIGTTIN或SIGTTOU)的反应。其二是受到父进程ptrace调用的控制,而暂时将处理器交给控制进程。
ZOMBIE:僵尸状态。表示进程结束但尚未消亡的一种状态。此时进程已经结束运行并释放掉大部分资源,但尚未释放进程控制块。
linux进程状态切换图
使用PS命令查看进程时可以看到,“S”进程是指处于阻塞状态,“R”进程处于就绪或运行状态。
进一步理解,linux 进行进程调度时是对就绪队列中的进程进行时间片的分配。而阻塞状态和挂起状态的进程都处在阻塞队列中,只有唤醒后才能加入就绪队列中等待内核的调度。

再来看CPU占有率是如何计算出来的。
首先需要明白的是只要设备上电CPU就不会闲着,总会有某个进程占用CPU运行,linux内核启动最终会启动cpu_idle进程,在该进程中会死循环调用schedule函数进行调度,即使就绪队列中没有进程可以调度,也就是说没有进程时RUNNING状态,CPU也会一直在cpu_idle中死循环。而我们常说的CPU被占用其实是指就绪队列中有进程被调度使用CPU,CPU占有率也就是该进程使用CPU的的一个百分比。

搞明白了以上两点,再反过来看我们的问题。为什么空循环进程CPU占用率高呢。
可以想象,空循环进程中虽然没做任何事情,却没有阻塞条件(如sleep),进程一直处于RUNNING状态,也就是说即使该进程时间片到了被切换,该进程还是处于就绪队列,等待下次调度。
linux内核的调度算法是很复杂的,根据UTLK介绍,关键因素在于时间片的分配和优先级的计算,优先级越高的进程时间片分配越长。
这里咱们去繁就简,假设linux内核调度算法仅仅是等时时间片调度,也就是说1s内所有运行进程时间片是相等的,其实就是将1s时间平分给就绪队列中的所有进程。所以问题的关键就是就绪队列中有多少个进程来平分这个时间了。
对我的设备中ps命令查看进程状态,发现只有空循环进程是RUNNING状态的。
但是由于运行top命令时top也是一个RUNNING进程,因此这时的linux内核仅仅对这2个进程进行来回调度,当然空循环进程的CPU占用率高了!

2 既然CPU一直被占用,CPU负载高低如何解释,空循环进程对系统有什么影响。

想明白空循环进程问题后我就对CPU负载高低产生了疑问,既然CPU一直有进程占用,也就是说CPU其实是一直满载的,为什么还有负载高低这一说。
我的理解是这样的,对于CPU来说,它一直是按照固定的频率取指令运行,没有负载高低之说,我们所说的负载高低其实是人感官的说法,比如top查看发现CPU占用率高,系统主要应用进程(如sh)反应慢,我们就说负载高。
打个形象点的比方,CPU可以比喻为家庭机器人,可以进行做饭 洗衣服 扫地,机器人做这些工作的速度是不变的。做饭10分钟完成,但是如果要求它做饭 洗衣服 扫地同时进行,站在机器人的角度来说,没有差别,速度是一样的。但是从我们的角度来看我们的主要应用-做饭,就发现它做饭慢了,我们就认为机器人是不是负载太高啦。
因此空循环对系统的影响也就好理解了,就会导致就绪队列中无意义的进程多了,但一个空循环进程影响不明显。
就绪队列中本来只有2个进程,主应用进程在1s内可以运行0.5s,但是如果有20个进程,则只可以运行0.05s,进程的执行响应效率也就下来了。
这里可以做一个简单的试验,在我的设备系统中启动几十个空循环进程,发现系统控制台反应都慢了,这就验证了以上说法。

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页