Linux之抢占式内核
作者: 来源:zz 发表时间:2008-05-08
浏览次数:
字号:大 中 小
SMP锁的目的是确保内核重入(re-entrance)安全。也就是,如果并行运行的进程需要访问内核资源,那么对这些资源访问必须安全、可靠。越精细的锁定粒度越能保证竞争进程运行的并行巡行能力越强。因为并行能力的提高需要减少堵塞(因为锁的争用),(而细粒度锁可减少阻塞机会)。
上述概念也同样适用于单处理器。对于I/O系统来说,如果将I/O设备看作一个独立的处理器,那么应用程序和I/O活动的并行运行无疑能提高系统吞吐量。提高抢占性,意味着高优先级的I/O范畴进程(I/O-bound process)会更频繁地被唤醒,从而提高了吞吐量。这样以来,虽然可能带来某些负作用,比如我们可能会需要更多的上下文切换和在内核关键路径上执行更多代码(关键路径直那些访问共享资源的代码路径,为了防止竞争条件需要上锁等操作),但是即使如此,我们仍然可以获得更大的系统吞吐量。
允许内核抢占的优势是显而易见的,标准内核迟早会引入抢占功能(目前已经具有)。抢占式内核有些实现中可以提供几微妙的响应时间,而更精确的实现则可把响应时间提高到几十分之一微秒。
环顾嵌入Linux生产商,MontaVista 和TimeSys提供了抢占式内核;REDSonic使用抢占点;LynuxWorks和红帽子使用RTLinux;Lineo 使用RTAI;OnCore通过和Linux系统调用兼容的API(和LynuxWorks利用LynuxOs一样)与在他们的抢占式微内核上。
运行一个Linux内核(可抢占的)达到实时目的。
抢占点
抢占点的核心思想是在特定点上调用调度程序去检查是否有更高优级的任务做好了运行准备,需要即刻运行。Molnar和Morton就是利用该思想,测量内核路径的执行时间,找到时间过长的路径插入调度检查点(等于打断了长路径)。你可以通过抢占补丁代码或是对比打过补丁的内核和先前未达补丁的内核,从中发现那些地方需要插入抢占点。抢占补丁看起来很象if (current ->need_resched) schedule()(这是用户空间抢占执行的典型情形);
使用Molnar的补丁要做的事情同上。下载补丁、编译新内核,Morton开发了针对2.4多个版本的补丁。而Molnar的补丁针对一些2.2版本内核和早期的2.4版本内核。
抢占式内核
抢占式内核使得用户程序在执行系统调用期间可以被抢占,从而使新被唤醒的高优先级进程能够运行。这种抢占并非可以在内核中任意位置都能安全进行,比如在临界区中的代码就不能发生抢占。临界区是指同一时间内不可以有超过一个进程在其中执行的指令序列。在Linux内核中这些部分需要用自旋锁保护。
MontaVista 和 TimeSys采用类似的方法建立抢占式内核。他们巧妙地将自旋锁的功能加强为也能防止抢占(2.6新内核中自选锁也是这样做的)。依靠该方法,抢占只能发生在未使用自旋锁的其它部分。当一个更高优先级的进程被唤醒,调度程序就能抢占低优先级进程正在执行的系统调用,只要此刻该系统调用代码没有被自旋锁保护——加上自旋锁意味不可抢占。
另外,使用抢占式内核,打断锁(解开再锁上)使得重新调度相比采用抢占点(低响应时间)补丁要简单。因为如果内核释放了一个锁,然后又再获得它,那么当锁被释放时,就会进行抢占检查。内核中有些地方需要上锁——比如一个循环——但不需要一直持有锁,可以每进行一次循环,锁就要被释放,然后再获得。
MontaVista实现抢占主要通过一个计数器(2.6版本中称它为抢占计数,用它可以跟踪锁定嵌套数),当自旋锁被获取时,该计数就增加1。当高优先级别的进程被唤醒,调度程序会检查抢占计数——检查是否为零——判断是否此刻允许抢占(如果为0,就可以发生抢占)。依靠这个计数器,当锁嵌套调用时,抢占机制能很好的工作,但是任何持有自旋锁的临界区域都不允许抢占,即使锁用来保护的是些无关资源。
TimeSys采用一个优先级遗传互斥量(priority inheritance mutex)。利用这种机制,一个高优先级的进程可以抢占一个持有不同资源互斥量的低优先级别的进程。另外因为采用了优先级遗传,所以持有互斥量的低优先级的进程不能无限推迟等待在该互斥量上的高优先级进程。这样以来解决了所谓的优先级翻转问题(Priority Inversion Problem)。