• 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 :进程状态和其所在的队列没有关系,设置进程状态和抢占总是有可能有间隙的

  • 相关阅读:
    vmware图形化安装kali iso镜像文件启动后黑屏问题解决方法
    8818驱动蓝屏报错解决方法
    v3ray提示已经在运行的错误
    逆向工程之安卓逆向- --- p命名法中插入一个新寄存器
    硬件黑客之无线电 --- Proxmark3 RDV4 套件
    硬件黑客之无线电 --- Plunder Bug LAN Tap
    硬件黑客之局域网安全 --- 鲨鱼杰克
    硬件黑客之无线电 --- Proxmark3 RDV4 套件
    硬件黑客之无线电 --- NFC Kill
    硬件黑客之无线电 --- 双频 WiFi RTL8812AU
  • 原文地址:https://www.cnblogs.com/openix/p/2952041.html
Copyright © 2020-2023  润新知