旅游回来了,继续写博客。最近没什么时间,而且过两天还要去桂林,算法相关的先放一放。写点操作系统相关的,轻松一点。这毕竟还算是本职工作。水平有限,对问题的理解可能有不正确的地方。还希望高手能够予以指正。
为啥要写线程调度:
1.作为一个it,经常被人问为什么电脑为什么越来越慢。但是就是这个简单的问题,却让人没法回答。
2.做运维经常要监控cpu空闲时间,了解线程调度,能更好的理解cpu时间的意义。
学过操作系统的都知道,单核的cpu的线程调度一般是一个圆盘队列,如果专业点叫做round robin。所有线程在里面轮转。每个线程一次最多只能占一定的时间片。
问题在于这些线程并不是均等的。有些需要及时地响应,有些需要较大的吞吐量。
针对这些需求,有两种解决办法:
一是增加线程的优先级,二是增加线程每次轮转所能得到的时间片。
第一种方式可以有效的提升线程的响应速度,第二种能够合理地增大线程的吞吐量。
这个原理本身是比较简单的,但是操作系统会根据不同情况动态地调整优先级和时间片。由于这两种方式的差异很大,因此造成了很多让人困惑的地方。
先说一下windows吧。
明确几个问题:
1.操作系统调度的最小单位是线程,而不是进程。linux调度的单位是进程是因为在linux里面,线程是另一种具有特定参数的进程,把两个进程的空间捏到了一起。实际上,还是对线程进行调度。
2.线程调度优先级和中断级别是两个概念。用户的线程基本都处在最低级的中断级别。
3.只有状态处于ready状态的线程才会参与调度。
4.绝大多数程序都是io-bound,cpu-bound的程序非常少。
能够形成cpu-bound的最大的可能就是程序陷入了一个非常大的循环。而且这个循环内不存在同步的io操作。
比如:
写了这样一个程序
我平时工作的电脑是一台vsphere下的win2008R2的虚拟机,有4颗虚拟cpu。
这个程序只能占4%的cpu。这是因为print 输出是io操作,等待print返回会让程序陷入休眠状态。
一旦把程序改成
pass
一旦运行,就会立刻把cpu飚到25%,占满一整颗cpu的时间。
5.windows调度的特征是主要以修改线程优先级为主。同linux的nice值不同,linux进程的nice值主要是决定时间片的大小。
优先级有两种,一种是静态优先级。或者叫初始优先级。继承于进程的优先级。一种是动态优先级,由操作系统根据线程的情况动态调整。
优先级一共0~31,32个级别。普通用户线程的优先级在1~15之间。动态调度不会超过15这个数值。一般程序默认的优先级是8。
我用c写了一个无限循环。它毫无压力地占掉一整颗cpu的时间。
我开了2个进程,并用process explorer把两个进程放到同一颗cpu。于是每个进程各站12.5%。
A.我把一颗cpu的优先级调高一点。那么这个程序就会占掉25%的cpu.而另一个进程就会停掉。cpu cycles完全不动。但是每4秒钟,那个优先级低的进程能够运行一下,走掉8*10^8左右的cpu cycles。
这说明出于瓶颈状态的调度完全是按照优先级来进行的。但是系统还会检查超过4秒钟没有能够得到运行的线程,把这些线程的优先级临时提升到15,让他们能够得到执行。
如果我开了3个进程放在一个cpu,同样提升一个进程的优先级。每4秒钟,优先级低的进程还是会走掉8*10^8左右的cpu cycles。这说明时间片的大小和进程的多少没有关系。
默认貌似时间片大小和cpu也无关。我用clockres测试了2个完全不同的cpu的机器,周期都是15.6ms。猜测基础时间片的计算单位是时间,而非cpu频率。
B.我点选了一个进程之后,两个进程的cpu获得的比例变成了2:1。如果我开了3个进程并放在一颗cpu,那么这3个进程获得cpu的比例变成了2:1:1。用process explorer查看线程的动态优先级,并没有发生变化。
这个现象说明了操作系统会把获得焦点的进程每次的时间片翻倍。
C.我开了一个空循环程序,再开一个有着print循环的程序。放在了同一颗cpu上面。有着print 循环的程序的动态优先级会在8和9之间徘徊。而我点选这个程序,会让这个程序的优先级在8到11之间徘徊。
按照《深入理解windows内核》里面的说法,等待的io或者信号量的到来会让这个线程的优先级提高。而这类程序到到前台以后,优先级会再增加2
D.如果我开了4个进程,把这4个进程放置在不同的cpu,那么会占掉100%的cpu。不过系统还是和以前一样流畅。这是因为这4个进程的优先级都是8。任何一个优先级高于8的进程都会抢占掉这4个进程的cpu时间。
但是当我把第二颗cpu上的进程的基本优先级调整到high:13。桌面的响应就会变得奇慢,每4秒才会对所做的操作产生一次反应。三个进程,或者调整其他cpu优先级都不会造成这种反应。
这说明我的电脑负责桌面的是第二颗cpu。如果这个cpu被占,那么调度程序会把它换一个cpu,但是为什么在第二颗cpu被高优先级线程占据的情况下,其他cpu被低优先级线程占据,这个线程没能够抢到优先级低的其他cpu的时间。还解释不清楚原因,大概这是调度一个逻辑上小bug吧。
最后要说明对于操作系统来说,cpu 100%其实没什么关系,优先级才是关键。但是对应用程序来说,偶尔过高的cpu可能说明你的程序存在逻辑上的问题。正常态过高的cpu可能会在峰值时候出现性能瓶颈。