cond_resched()函数

整理关于cond_resched的帖子:
来自http://linux.chinaunix.net/bbs/thread-1056984-1-1.html
 
1楼 发表于 2009-1-8 02:11 
 
// preempt_disable()会使preempt_count加1
#define preempt_disable()
do {
        inc_preempt_count();
        barrier();
} while (0)

//  只检查了PREEMPT_ACTIVE标志
int __sched _cond_resched(void)
{
        if (need_resched() && !(preempt_count() & PREEMPT_ACTIVE) &&
                                        system_state == SYSTEM_RUNNING) {
                __cond_resched();
                return 1;
        }
        return 0;
}

如上代码,假如先调用了preempt_disable使preempt_count加1,显示禁用了内核抢占,
接着调用了某个会调用cond_resched的函数,然后_cond_resched函数只会检查PREEMPT_ACTIVE
标志,对0~7位的抢占计数器视而不见,使得该函数会调用schedule(),发生内核抢占。从而
忽略掉preempt_disable对于内核抢占的影响。

不知道我以上推断是否正确?如果是这样,那为什么要这么设计啊?还请各位指教一下。

 
2楼 发表于 2009-1-8 23:27 
 
首先确认一点,  如果使用了preempt_disable(), 是不允许调用cond_resched的, 如果调用, schedule()应该会打印出错信息.
这个很好理解, cond_resched()的目的是提高系统实时性, 主动放弃cpu供优先级更高的任务使用, 如果调用了preempt_disable(), 不允许抢占, 就不应该调用有cond_resched()的函数.

2.6.28内核, schedule()会在这里捕获这个错误:
asmlinkage void __sched schedule(void)
{
        struct task_struct *prev, *next;
        unsigned long *switch_count;
        struct rq *rq;
        int cpu;

need_resched:
        preempt_disable();
        cpu = smp_processor_id();
        rq = cpu_rq(cpu);
        rcu_qsctr_inc(cpu);
        prev = rq->curr;
        switch_count = &prev->nivcsw;

        release_kernel_lock(prev);
need_resched_nonpreemptible:

        schedule_debug(prev);

static inline void schedule_debug(struct task_struct *prev)
{
       /* 如果preempt值不等于1, 说明出错, 等于1而不是0是因为schedule()中要关闭抢占, 在上面有这句 */
        if (unlikely(in_atomic_preempt_off() && !prev->exit_state))
                __schedule_bug(prev);

怎么解决这个preempt_disable()和cond_resched()的冲突?
preempt_disable()是一个最底层的接口,  如果你要使用这个接口关抢占, 必须要熟悉会调用cond_resched()的函数, 或者知道如何调试这个bug, 这种bug发现和解决应该不难.
一般都是通过mutex, spinlock, 关中断, 关软中断等方法, 顺带关抢占. 如果是上面的几种情况, 最新的内核提供:
extern int cond_resched_lock(spinlock_t * lock);
extern int cond_resched_softirq(void);
static inline int cond_resched_bkl(void);
来代替cond_resched(), 使得在不同情况下使用更安全.

Leave a Reply

Your email address will not be published. Required fields are marked *