PREEMPT_ACTIVE

thread_info中的preempt_count域设置当前进程是否可被抢占,但是我们还得注意下内核中会用到preempt_count() & PREEMPT_ACTIVE,这就是判断preempt_count 的PREEMPT_ACTIVE是否被置位,preempt_count的PREEMPT_ACTIVE位只有在内核抢占中才会被置位。

__irq_svc:
    svc_entry

#ifdef CONFIG_PREEMPT
    get_thread_info tsk
    ldr r8, [tsk, #TI_PREEMPT]      @ get preempt count
    add r7, r8, #1          @ increment it
    str r7, [tsk, #TI_PREEMPT]
#endif

    irq_handler
#ifdef CONFIG_PREEMPT
    str r8, [tsk, #TI_PREEMPT]      @ restore preempt count
    ldr r0, [tsk, #TI_FLAGS]        @ get flags
    teq r8, #0              @ if preempt count != 0
    movne   r0, #0              @ force flags to 0
    tst r0, #_TIF_NEED_RESCHED
    blne    svc_preempt
#endif
    ldr r4, [sp, #S_PSR]        @ irqs are already disabled
#ifdef CONFIG_TRACE_IRQFLAGS
    tst r4, #PSR_I_BIT
    bleq    trace_hardirqs_on
#endif
    svc_exit r4             @ return from exception
 UNWIND(.fnend      )    
ENDPROC(__irq_svc)

    .ltorg

#ifdef CONFIG_PREEMPT
svc_preempt:
    mov r8, lr
1:  bl  preempt_schedule_irq        @ irq en/disable is done inside
    ldr r0, [tsk, #TI_FLAGS]        @ get new tasks TI_FLAGS
    tst r0, #_TIF_NEED_RESCHED
    moveq   pc, r8              @ go again
    b   1b
#endif

考虑下:

preempt_count为0,本进程可被抢占,中断处理后发现有优先级高的进程,因此,thread_info中的flags字段的TIF_NEED_RESCHED位被置位,

所以调度器重新选择进程运行,进入preempt_schedule_irq函数

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);
                local_irq_enable();
                schedule();
                local_irq_disable();
                sub_preempt_count(PREEMPT_ACTIVE);

                /*    
                 * Check again in case we missed a preemption opportunity
                 * between schedule and now.
                 */
                barrier();
        } while (need_resched());
}

此处会置位preempt_cout的PREEMPT_ACTIVE位,重点看下schedule函数中的如下几行

        if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) {
                if (unlikely(signal_pending_state(prev->state, prev)))
                        prev->state = TASK_RUNNING;
                else
                        deactivate_task(rq, prev, 1);
                switch_count = &prev->nvcsw;
        }

若state为running,不会出问题,但是如果state不是running就可能会出问题,试想:

#define __wait_event(wq, condition)                                                         \                                          
do {                                                                                                        \
        DEFINE_WAIT(__wait);                                                                   \
                                                                                                              \
        for (;;) {                                                                                          \
                prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE);   \
                if (condition)                                                                           \
                        break;                                                                            \
                schedule();                                                                             \
        }                                                                                                    \
        finish_wait(&wq, &__wait);                                                              \
} while (0)
第一种情形:prepare_to_wait()中如果只设置了task_struct的state后立即发生中断,中断返回时判断该进程可以被抢占,如果我们没有将当前进程的thread_info:preempt_count中置位PREEMPT_ACTIVE,则当前进程将被剔除运行队列,这下问题来了:我们还没有将当前进程挂到等待队列,那么该进程将不可能再次运行。因此如果在内核态发生中断,且当前进程判断可以被抢占,则先调用preempt_schedule(),在preempt_schedule中会将thread_info:preempt_count加上PREEMPT_ACTIVE,而后在调用schedule(),这样即使当前进程还没有被挂入等待队列,也不会被剔除,仍然可以正常运行。

第二种情形:prepare_to_wait()中已挂入等待队列,顺利运行schedule(),那么当前进程将会被剔除运行队列,但是当条件满足时可以被唤醒。

第三种情形:A进程在等待condition满足,B进程某时设置condition,并唤醒等待队列上的A,A此时再次运行到prepare_to_wait(),此时发生中断,如果我们没有判定是否由于内核抢占而进行schedule调用(即判定PREEMPT_ACTIVE位),则由于prev->state非零(非running),则当前进程会被剔除运行队列,而由于此后可能再也没有唤醒该进程的其它进程,该进程将永久的不到运行。

 

keep in mind :进程状态和其所在的队列没有关系,设置进程状态和抢占总是有可能有间隙的

posted on 2013-03-09 22:01  阿加  阅读(1716)  评论(1编辑  收藏

导航