1.相关数据结构及函数如下:
struct thread_info中
{
unisgned int preempt_count;//线程抢占引用计数器,为0才能被抢占
}
2.preempt_disable的定义
#define preempt_disable() \
do{ \
inc_preempt_count(); \//增加preempt_count
barrier(); \//内存屏障
}while(0)
3.barrier:内存屏障
内存屏障出现因为编译器gcc或现在的处理器常会自作聪明地对指令序列进行一些处理,比如数据缓存,读写指令乱序执行等等。如果优化对象是普通内存,那么一般会提升性能而且不会产生逻辑错误。但如果对 I/O操作进行类似优化很可能造成致命错误。
4. 常见的调度点
1)进程被阻塞时比如申请资源时被阻塞
2)调整参数时 比如通过sched_setscheduler() ,nice()等函数调整进程的调度策略,静态优先级时
3)睡眠进程被唤醒时:比如wake_up唤醒等待队列中的进程时,如果该进程具有更高优先级则会设置当前进程TIF_NEED_RESCHED,如果允许内核态抢占,则会调度一次
4)中断处理完时 如果中断处理过程中设置了TIF_NEED_SCHED标志,中断返回时,不论是要返回内核态还是用户态,都会发生一次抢占。当然,在这也会检查有没有软中断需要处理。
5)执行了preempt_enable()函数。
5. linux下有两种调度方式:
1)显式调度,进程自己因为缺少相应的所申请的资源,显式调用调度器,让出处理器,比如:内核申请的信号阻塞了,自旋锁锁住了。
2)隐式调度,整个linux系统在运行过程中的非显示的调用调度器,这又分两种情况:
A)用户态抢占调度 比如:在系统调用,中断处理,异常处理返回用户态时,该进程的时间片已经用完。
B)内核态抢占调度 比如:当前内核态执行过程中事先没有禁止内核态抢占,有中断产生时,中断处理 又产生了更高级优先进程,那么就会直接抢占前面的内核态执行体。
6. 抢占式内核中有三处地方需要显示的禁用抢占:
1) 操作Per-CPU变量的时候,比如smp_processor_id()就是这一类问题,但一个进程被抢占后重新调度,有可能调度到其他的CPU上去,这时定义的Per-CPU变量就会有问题。下面是一个例子:
2) 必须保护CPU的状态。这类问题是体系结构依赖的。例如,在x86上,进入和退出FPU就是一种临界区,必须在禁抢占的情况下使用。
3) 获得和释放锁必须在一个进程中实现。也就是说一个锁被一个进程持有,也必须在这个进程中释放。
7.禁用/使能抢占的函数主要有:
1)spin_lock()/spin_unlock() spin_lock_irq()/spin_unlock_irq()
2)disable_preempt()/enable_preempt()(禁止或使能内核抢占)调用下面的inc_preempt_count()/dec_preempt_count(),并且加入了memory barrier。
3)inc_preempt_count()/dec_preempt_count()
4)get_cpu()/put_cpu()