内核抢占:在2.6内核加入了抢占的能力,就是说调度程序有办法在一个内核级的任务正在执行的时候从新调度。但是必须要保证重新调度要安全,这样就要靠一把锁来保证了。
计数器(thread_info.[reempt_count):其实也可以把他叫成锁的,它的初值定义为0,每当使用锁的时候加1。
start_kernel 开始后的第一个C 函数. include/linux/smp_lock.h中有宏定义: #ifdef CONFIG_LOCK_KERNEL #define kernel_locked() (current->lock_depth >= 0) extern void __lockfunc lock_kernel(void) __acquires(kernel_lock); extern void __lockfunc unlock_kernel(void) __releases(kernel_lock); #else #define lock_kernel() do { } while(0) #define unlock_kernel() do { } while(0) #define release_kernel_lock(task) do { } while(0) #define reacquire_kernel_lock(task) 0 #define kernel_locked() 1 #endif lock_kernel 的真实身份还需要CONFIG_LOCK_KERNEL 确定. 内核使用了arch/arm/configs/s3c2410_defconfig作为默认的配置文件,Code maturity level options 选项下没有设置CONFIG_LOCK_KERNEL 项.所以在这里lock_kernel应该就是do { } while(0) 的空操作. 搞清楚了,但还是要看一下CONFIG_LOCK_KERNEL 定义的情况下lock_kernel的情况:lib/kernel_lock.c中有定义也是要根据宏的定义具体判断使用哪个实现. CONFIG_PREEMPT_BKL 判断使用'big kernel semaphore还是'big kernel lock; 即使使用 'big kernel lock CONFIG_PREEMPT(竞态) 宏不同lock_kernel实现也不同; 2410这种单处理器不会有多个cpu竞争的情况,所以不需要大内核锁/信号量来解决资源竞争问题...
#ifdef CONFIG_PREEMPT_BKL
static DECLARE_MUTEX(kernel_sem); void __lockfunc lock_kernel(void) { struct task_struct *task = current; int depth = task->lock_depth + 1;
if (likely(!depth)) down(&kernel_sem); task->lock_depth = depth; } void __lockfunc unlock_kernel(void) { struct task_struct *task = current; BUG_ON(task->lock_depth < 0); if (likely(--task->lock_depth < 0)) up(&kernel_sem); } #else static __cacheline_aligned_in_smp DEFINE_SPINLOCK(kernel_flag);
#ifdef CONFIG_PREEMPT static inline void __lock_kernel(void) { preempt_disable(); if (unlikely(!_raw_spin_trylock(&kernel_flag))) { if (preempt_count() > 1) { _raw_spin_lock(&kernel_flag); return; }
do { preempt_enable(); while (spin_is_locked(&kernel_flag)) cpu_relax(); preempt_disable(); } while (!_raw_spin_trylock(&kernel_flag)); } } #else static inline void __lock_kernel(void) { _raw_spin_lock(&kernel_flag); } #endif
void __lockfunc lock_kernel(void) { int depth = current->lock_depth+1; if (likely(!depth)) __lock_kernel(); current->lock_depth = depth; }
#endif
我们现在来看看lock_kernel()的具体代码啦(2.6.10版本的) #ifndef __LINUX_SMPLOCK_H #define __LINUX_SMPLOCK_H #include <linux/config.h> #include <linux/sched.h> #include <linux/spinlock.h> #ifdef CONFIG_LOCK_KERNEL #define kernel_locked() (current->lock_depth >= 0) extern int __lockfunc get_kernel_lock(void); extern void __lockfunc put_kernel_lock(void); /* * Release/re-acquire global kernel lock for the scheduler */ #define release_kernel_lock(tsk) do { / if (unlikely((tsk)->lock_depth >= 0)) / put_kernel_lock(); / } while (0) /* * Non-SMP kernels will never block on the kernel lock, * so we are better off returning a constant zero from * reacquire_kernel_lock() so that the compiler can see * it at compile-time. */ #ifdef CONFIG_SMP #define return_value_on_smp return #else #define return_value_on_smp #endif static inline int reacquire_kernel_lock(struct task_struct *task) { if (unlikely(task->lock_depth >= 0)) return_value_on_smp get_kernel_lock(); return 0; } extern void __lockfunc lock_kernel(void) __acquires(kernel_lock); extern void __lockfunc unlock_kernel(void) __releases(kernel_lock); #else #define lock_kernel() do { } while(0) #define unlock_kernel() do { } while(0) #define release_kernel_lock(task) do { } while(0) #define reacquire_kernel_lock(task) 0 #define kernel_locked() 1 #endif /* CONFIG_LOCK_KERNEL */ #endif /* __LINUX_SMPLOCK_H */ 这个是对lock_kernel()函数的宏定义,#ifdef CONFIG_LOCK_KERNEL这里判断是否支持内核锁,如果不支持的话,lock_kernel就不做任何事情,我们来具体分析一下当配置了CONFIG_LOCK_KERNEL时lock_kernel()做些什么!我们直接看看这行代码extern void __lockfunc lock_kernel(void) __acquires(kernel_lock); void __lockfunc lock_kernel(void) { int depth = current->lock_depth+1; if (likely(!depth)) __lock_kernel(); current->lock_depth = depth; } 这里可以看到有个标志lock_depth存在,对于这个标志,它的初始值为-1,如果自加后发现其实为0时,说明kernel_flag这个内核全局锁成功被这个进程所占有(current->lock_depth+1这个意味着0号进程的init_task.lock_depth加1)。接着就来到了__lock_kernel();这里的调用。我们来展开看看它的代码吧! #ifdef CONFIG_PREEMPT static inline void __lock_kernel(void) { preempt_disable(); if (unlikely(!_raw_spin_trylock(&kernel_flag))) { /* * If preemption was disabled even before this * was called, there's nothing we can be polite * about - just spin. */ if (preempt_count() > 1) { _raw_spin_lock(&kernel_flag); return; } /* * Otherwise, let's wait for the kernel lock * with preemption enabled.. */ do { preempt_enable(); while (spin_is_locked(&kernel_flag)) cpu_relax(); preempt_disable(); } while (!_raw_spin_trylock(&kernel_flag)); } } #else /* * Non-preemption case - just get the spinlock */ static inline void __lock_kernel(void) { _raw_spin_lock(&kernel_flag); } #endif 看到一上来就对CONFIG_PREEMPT进行判断,这个是判断内核是否支持抢占。如果是抢占大的话,就执行上面的__lock_kernel(),我们来看看第一行语句, preempt_disable(); #define preempt_disable() / do { / inc_preempt_count(); / barrier(); / } while (0) 我们来看看inc_preempt_count();这个函数是什么内容。 #define inc_preempt_count() / do { / preempt_count()++; / } while (0) 我再进一步再往里看看。 #define preempt_count() (current_thread_info()->preempt_count) 终于看到了是对current_thread_info()->preempt_count自增。(在start_kernel来说就是init_thread_info.preempt_count加1)这样我们又回到了__lock_kernel函数了,对于下面代码的分析不太清楚,这里就不进行讲解了。接着我们来到了没有进行宏定义的else部分。 #else /* * Non-preemption case - just get the spinlock */ static inline void __lock_kernel(void) { _raw_spin_lock(&kernel_flag); } #endif 这里就是简单的对kernel_flag这个全局变量加锁。好了,到这里我们算是把lock_kernel()分析了一遍了。其实总的来说,对于有抢占的,我们就不用用kernel_flag这个全局变量。这个全局变量其实是在2.4内核没有抢占功能内核的代码使用的。
|