现在的位置: 首页 > 综合 > 正文

linux2.6内核模式下的抢占调度

2013年04月14日 ⁄ 综合 ⁄ 共 3911字 ⁄ 字号 评论关闭

1,不可抢占内核(用户模式抢占)

1.a, 内核中显示调用schedule(), 主动放弃cpu,申请调度。

比如说,刚才进ring0就是因为系统调用sleep()进来的,那么这个时候,t1的状态被设置为阻塞,系统开始新的调度,选择一个新任务t2,返回到t2的用户态上下文

1.b, 在用户态或者内核态发生中断异常,中断异常处理完毕以后,不会发生调度,依然返回t1的内核栈上下文,t1继续执行,在返回用户态的时候,发生调度

2,可抢占内核下(内核模式抢占)

2.a, 显示调用schedule(), 主动放弃cpu,申请调度。

比如说,刚才进ring0就是因为系统调用sleep()进来的,那么这个时候,t1的状态被设置为阻塞,系统开始新的调度,选择一个新任务t2,返回到t2的内核态或者用户态上下文

2.b, 在用户态或者内核态发生中断异常,中断异常处理完毕以后,会发生调度,返回到新任务的上下文(内核栈或者用户栈都有可能)继续执行。

归纳一下:

1,系统理论上可以在他认为安全的任何地方调度不同的任务。

2,中断,异常处理完毕返回(内核态s1或者用户态s2)的时候,系统调用返回(用户态)s3的时候,都可能触发schedule(), 还有就是在内核中阻塞点s4显示调用schedule()。

3.a,非抢占内核认为返回用户态的时候,或者进程主动调用schedule()的时候,也就是s2,s4是安全的,所以在这2个进行调度,返回的位置只有到用户态(proof)。

3.b,抢占内核认为s1,s2,s3,s4都是安全的(判定preempt_count),所以这些位置都可以调度,返回的位置也就可能是内核态也可能是用户态。

内核线程的实现也有了依据:

void kernel_thread(void *)

{
   for (;;)
   {
     // todo: some repeat works,
     // which can be interruptted & resumed
     //...
   }
}

补充:

内核态发生中断的时候,不会借助tss换入内核态的ss和esp,直接借助当前任务的内核栈完成处理。

用户态发生中断的时候,借助tss换入当前任务内核态的ss和esp,进行中断处理。

/*
* Linux抢占式内核就是由Robert Love修改实现的。
* 在他的书中有如下描述:

User Preemption

User preemption occurs when the kernel is about to return to user-space, 
need_resched is set, and therefore, the scheduler is invoked. 
If the kernel is returning to user-space, it knows it is in a safe quiescent state. 
In other words, if it is safe to continue executing the current task, 
it is also safe to pick a new task to execute. 
Consequently, whenever the kernel is preparing to return to user-space 
either on return from an interrupt or after a system call, 
the value of need_resched is checked. 
If it is set, the scheduler is invoked to select a new (more fit) process to execute.
Both the return paths for return from interrupt and return from system call 
are architecture dependent and typically implemented in assembly 
in entry.S (which, aside from kernel entry code, also contains kernel exit code).
In short, user preemption can occur
When returning to user-space from a system call
When returning to user-space from an interrupt handler

Kernel Preemption

The Linux kernel, unlike most other Unix variants and many other operating systems, 
is a fully preemptive kernel. 
In non-preemptive kernels, kernel code runs until completion. 
That is, the scheduler is not capable of rescheduling a task while it is in the kernel. 
kernel code is scheduled cooperatively, not preemptively. 
Kernel code runs until it finishes (returns to user-space) or explicitly blocks. 
In the 2.6 kernel, however, the Linux kernel became preemptive: 
It is now possible to preempt a task at any point, 
so long as the kernel is in a state in which it is safe to reschedule.

So when is it safe to reschedule? 
The kernel is capable of preempting a task running in the kernel 
so long as it does not hold a lock. 
That is, locks are used as markers of regions of non-preemptibility. 
Because the kernel is SMP-safe, if a lock is not held, 
the current code is reentrant and capable of being preempted.

The first change in supporting kernel preemption 
was the addition of a preemption counter, 
preempt_count, to each process's thread_info. 
This counter begins at zero and increments once for each lock that is acquired 
and decrements once for each lock that is released. 
When the counter is zero, the kernel is preemptible. 
Upon return from interrupt, if returning to kernel-space, 
the kernel checks the values of need_resched and preempt_count. 
If need_resched is set and preempt_count is zero, 
then a more important task is runnable 
and it is safe to preempt. 
Thus, the scheduler is invoked. 
If preempt_count is nonzero, a lock is held and it is unsafe to reschedule. 
In that case, the interrupt returns as usual to the currently executing task. 
When all the locks that the current task is holding are released, 
preempt_count returns to zero. 
At that time, the unlock code checks whether need_resched is set. 
If so, the scheduler is invoked. 
Enabling and disabling kernel preemption is sometimes required in kernel code 
and is discussed in Chapter 9


Kernel preemption can also occur explicitly, 
when a task in the kernel blocks or explicitly calls schedule(). 
This form of kernel preemption has always been supported 
because no additional logic is required to ensure that 
the kernel is in a state that is safe to preempt.
It is assumed that the code that explicitly calls schedule() 
knows it is safe to reschedule.
Kernel preemption can occur When an interrupt handler exits, 
before returning to kernel-space When kernel code becomes preemptible again
If a task in the kernel explicitly calls schedule()
If a task in the kernel blocks (which results in a call to schedule())
*/ 

抱歉!评论已关闭.