1.线程描述符:
struct thread_info {
struct task_struct *task;
struct exec_domain *exec_domain;
__u32 flags;
__u32 status;
__u32 cpu;
int saved_preempt_count;
...
};
重要字段解释:
flags: 该字段中有一个位是用于设置内核是否应该被重新调度的标志–need_resched,set_tsk_need_resched()和clear_tsk_need_resched()函数实际上就是对该字段的某一位进行操作(第31位 ?)
saved_preempt_count: 内核此时是否可以进行抢占的标志,为0表示当前进程不持有锁,可以被抢占.大于0则表示此时持有锁,不能被抢占
preempt_counter 字段是32位的, 除了抢占计数器之外还包括其他标志位, 只要 preempt_counter 整体不为0, 就不能进行内核抢占, 这个设计一下子简化了对众多不能抢占的情况的检测:
Bit 0-7: 抢占计数器, 表示显式禁用内核抢占的次数
Bit 8-15: 软中断计数器, 记录可延迟函数被禁用的次数
Bit 16-27: 硬中断计数器, 表示中断处理程序的嵌套数, irq_enter()递增它, irq_exit()递减它
Bit 28: PREEMPT_ACTIVE 标志, 内核抢占的标志
2.schedule()函数被调度的时机:
1.内核从系统调用或中断处理程序返回时:
1.内核要返回到用户空间时:
如果当前进程的thread_info结构中的need_resched标志被设置,那么就可以调用schedule(),因为内核返回用
户空间时,它知道自己是安全的,因为既然它可以继续去执行当前进程,那么它当然可以再去选择一个新的进程去执行
2.内核要返回内核空间时:
如果当前进程的thread_info结构体中的need_resched标志被设置而且saved_preempt_count为0时, 通过调用preempt_schedule_irq()函数来间接调用schedule()函数:
asmlinkage void __sched preempt_schedule_irq(void)
{
struct thread_info *ti = current_thread_info();
/* Catch callers which need to be fixed */
BUG_ON(ti->preempt_count || !irqs_disabled());
do {
add_preempt_count(PREEMPT_ACTIVE); //设置thread_info->saved_preempt_count变量的第28位, 即PREEMPT_ACTIVE标志
local_irq_enable();
schedule(); //调度
local_irq_disable();
sub_preempt_count(PREEMPT_ACTIVE);
barrier();
} while (need_resched());
}
此时thread_info->saved_p