===分类===
抢占可以分为两种方式,一种是用户态抢占,一种是内核态抢占。
=== 用户态抢占 ===
用户态抢占,可以表现为用户态的多进程或者多线程特性。
多进程特性,这个很好理解。比如你可以一边听音乐一边上网浏览网页甚至再二点还可以开一窗口看电影。对于计算机来说以上各功能可以并发执行,互不影响。
再极端点,你可以写一简单应用程序,让其做死循环,系统依然会表示木有压力,你照样可以让其干其它的事情。
也就是说,用户态的多进程抢占是再自然不过的事情了,否则都不好意思说这是个现代化的操作系统。
多线程特性,编写过程序的会比较容易理解的。在一个应用程序内,可以开一个线程处理网络事务,还可以开一个线程处理用户输入事务,这样二者可以并发执行,不会出现在处理网络事务时卡死用户输入事务的糟糕用户体验。
早期的linux系统在用户态是不支持多线程的。
后来随着线程库的引入,可以支持多线程编程,但是此种模式下的多线程并不是抢占式的,也就是说一个线程需要主动让出对CPU的占用才能被切换到另外一个线程,即在一个线程中出现死循环会卡死该进程的其它所有线程,且此时的切换是在用户态来做的,kernel都不知道有这么一回事,kernel管理与调度是以该包含了多个线程的进程为实体的,这是一个"一对多”的模型。
再后来,出现了"一对一"的模型。一个用户态的线程对应一个内核线程,管理由用户态的线程库来做,而调度则交给了kernel来做,此时线程则是抢占式的了,即一个线程出现死循环后其它线程仍然可以被调度执行。这种模式也就是我们现在做多线程编程时用到的模式。
=== 内核态抢占 ===
早期的内核时不支持抢占的,这个就不说了。
内核态抢占分两种情形来讲。
其一,用户态程序陷入内核态之后。
当用户态程序程序通过系统调用或者中断方式而陷入内核态之后,如果内核配置编译时没有打开抢占,那么该运行路径一直要运行到返回用户态之前才做调度,早期的内核就是这样子玩的,很显然,若该运行路径有一个非常耗时的操作,则整个系统都将变得卡顿,再极端点,若该运行路径出现了死循环,对于单CPU系统,那么系统立马就挂掉了。另外一种情形,如果内核配置编译时打开了抢占,那么即便是在内核态运行,系统也会在合适的时机进行任务调度,并不需要等到返回用户态之前的那段时间再做,因此系统的实时性有很大的提高,一条执行路径并不会卡死其它的执行路径。
其二,内核线程的运行。
内核线程是运行在内核态的可调度的一个实体。在没有打开抢占的情形下,会对系统的实时性有影响,在死循环的极端情况下,对于单CPU系统,那么系统会挂掉。在打开抢占的情况下,一个内核线程出现了死循环,系统可以继续进行调度,不会出现卡死的情况。