用户抢占
内核即将返回用户空间的时候,如果need_resched被设置,会导致schedule()被调用,此时就会发生用户抢占。在内核返回用户空间的时候,它知道自己是安全的,因为既然它可以继续去执行当前进程,那么它当然可以再去选择一个新的进程去执行。所以内核无论是从中断处理程序还是在系统调用后返回,都会检查need_resched标志。如果它被设置了,那么,内核就会选择一个其他(更合适的)进程投入运行。从中断处理程序或系统调用返回的代码都是跟体系结构相关的,在entry.S文件中通过汇编语言来实现。
简而言之,用户抢占在以下情况时发生:
从系统调用返回用户空间;
从中断处理程序返回用户空间;
内核抢占
与其他大部分的unix变体和其他大部分的操作系统不同,Linux完整地支持内核抢占。在不支持内核抢占的内核中,内核代码可以一直执行,到它完成为止。也就是说,调度程序没有办法在一个内核级的任务正在执行的时候重新调度--内核中的各任务是协作方式调度的,不具备抢占性。内核代码一直要执行到完成(返回用户空间)或明显的阻塞为止。在2.6版的内核中,内核引入了抢占;现在,只要重新调度是安全的,那么内核就可以在任何时间抢占正在执行的任务。
那么,什么时候重新调度才是安全的呢?只要没有持有锁,内核就可以进行抢占。锁是非抢占区的域的标志。由于内核是支持SMP的,所以,如果没有持有锁,那么正在执行的代码就是可重新导入的,也就是可以抢占的。
为了支持内核抢占所作的第一处变动就是为每个进程的thread_info引入了preempt_count计数器。该计数器初始值为0,每当使用锁的时候数值加1,释放锁的时候数值减1。当数值为0的时候,内核就可以执行抢占。从中断返回内核空间的时候,内核会检查need_resched和preempt_count的值。如果need_resched被设置,并且preempt_count为0的话,这说明有一个更为重要的任务需要执行并且可以安全地抢占,此时调度程序就会被调用。如果preempt_count不为0,说明当前任务持有锁,所以抢占是不安全的。此时,就会像通常那样直接从中断返回当前执行进程。如果当前进程持有的锁都被释放了,那么preempt_count就会重新为0.此时,释放锁的代码会检查need_resched是否被设置。如果是的话,就会调用调度程序。有些内核代码需要允许和禁止内核抢占,相关内容不在这里讨论。
如果内核汇中的进程被阻塞了,或它显示地调用了schedule(),内核抢占也会显式地发生。这种形式的内核抢占从来都是受支持的。因为根本无需额外的逻辑来保证内核可以安全地被抢占。如果内核代码显式地调用了schedule(),那么它应该清楚自己是可以安全地被抢占的。
内核抢占会发生在:
当从中断处理程序返回内核空间的时候;
当内核代码再一次具有可抢占性的时候;
如果内核中的任务显式地调用schedule();
如果内内核中的任务阻塞(这同样也会导致调用schedule())
阅读(1238) | 评论(0) | 转发(0) |