• 内核抢占实现(preempt) 【转】


    转自:http://blog.chinaunix.net/uid-12461657-id-3353217.html

    一、什么叫抢占
    所谓抢占,说白了就是进程切换。
    linux的用户空间,进程A在执行中,来(硬?)中断打断A,从中断处理程序返回时,如果有更高优先级进程B在排队的话,那么执行进程B。 用户空间下进程总是可抢占的


    在linux的内核空间就不一定了,linux 2.4是不可抢占的,实时性就会降低,如下面这个样子:


    二、抢占的API

    preempt_enable()  开启抢占
    preempt_disable() 禁止抢占

    内核中每个进程数据结构里有一个计数器preempt_count
    抢占的开启与禁止,操作当前进程的preempt_count
    内核在进行进程调度的时候,只要prempt_count为0,内核就可以进行抢占。
        struct thread_info {
            struct task_struct *task; /* main task structure */
            ............//省略
            int     cpu;              /* cpu we're on */
            int     preempt_count;    /* 0 => preemptable,  <0 => BUG */
        };

        #define preempt_enable()
        do {
            preempt_enable_no_resched();
            barrier();
            preempt_check_resched();
        } while (0)

        #define preempt_disable()
        do {
            inc_preempt_count();
            barrier();
        } while (0)

        #define preempt_enable_no_resched()
        do {
            barrier();
            dec_preempt_count();
        } while (0)

        #define inc_preempt_count() add_preempt_count(1)
        #define dec_preempt_count() sub_preempt_count(1)
        #define add_preempt_count(val) do { preempt_count() += (val); } while (0)
        #define sub_preempt_count(val) do { preempt_count() -= (val); } while (0)
        #define preempt_count() (current_thread_info()->preempt_count)

    三、发生抢占的时机
    linux进程调度的核心函数是 schedule(),进程调度就是在这里做的。
    schedule的调用分为主动调用和被动调用。
    主动调用是指内核显示的直接去调用shedule(),如当前进程调用了可休眠函数,里面会调用schedule
    被动调用是指在系统调用、中断处理或异常处理结束之后,由相应的回调函数调用schedule
    判断完当前进程是否可抢占,才会接着去调用schedule()

    只看了看中断返回时schedule被动调用的情况
    至于主动调用的地方就太多了,什么进程结束,pause等等,没耐心看了。。。

    3.1 从中断返回时

    首先是从中断处理程序do_IRQ()返回后,会调用ret_from_except() (看《PowerPC中断相关知识》)
    ret_from_except()里要先check一下,判定前面被中断的执行体是运行在用户空间还是内核空间,
    在决定返回到用户空间或内核空间

    用户空间的话:(现在知道为什么用户空间程序总是可抢占了吧) 
    ret_from_except 
       --> user_exc_return 
            --> do_work 
              --> 调用 do_signal 和 schedule

    内核空间的话:(编译内核时要打开可抢占选项才行)
    ret_form_except 
        --> resume_kernel 
            --> preempt_schedule_irq 
              --> schedule

    .globl ret_from_except
    ret_from_except:
    LOAD_MSR_KERNEL(r10,MSR_KERNEL)  //将MSR_KERNEL常量设置到MSR,以禁止外部中断
    SYNC                             //Some chip revs have problems here...
    MTMSRD(r10)                      //disable interrupts

    lwz r3,_MSR(r1)                  //读栈中的MSR[PR],Returning to user mode?
    andi. r0,r3,MSR_PR
    beq resume_kernel

    user_exc_return:                   //r10 contains MSR_KERNEL here
    rlwinm r9,r1,0,0,(31-THREAD_SHIFT) //Check current_thread_info()->flags
    lwz r9,TI_FLAGS(r9)
    andi. r0,r9,(_TIF_SIGPENDING|_TIF_RESTORE_SIGMASK|_TIF_NEED_RESCHED)
    bne do_work
    restore_user:

    #ifdef CONFIG_PREEMPT
    b restore

    resume_kernel:
    rlwinm r9,r1,0,0,(31-THREAD_SHIFT) /* check current_thread_info->preempt_count */
    lwz r0,TI_PREEMPT(r9)
    cmpwi 0,r0,0                       /* if non-zero, just restore regs and return */
    bne restore
    lwz r0,TI_FLAGS(r9)
    andi. r0,r0,_TIF_NEED_RESCHED
    beq+ restore
    andi. r0,r3,MSR_EE                /* interrupts off? */
    beq restore                       /* don't schedule if so */
    1: bl preempt_schedule_irq
    rlwinm r9,r1,0,0,(31-THREAD_SHIFT)
    lwz r3,TI_FLAGS(r9)
    andi. r0,r3,_TIF_NEED_RESCHED
    bne- 1b
    #else
    resume_kernel:
    #endif /* CONFIG_PREEMPT */
    ////////////////////////////////////////////////////////////////////////////////////
    do_work:            /* r10 contains MSR_KERNEL here */
        andi.   r0,r9,_TIF_NEED_RESCHED
        beq do_user_signal

    do_resched:         /* r10 contains MSR_KERNEL here */
        ori r10,r10,MSR_EE
        SYNC
        MTMSRD(r10)     /* hard-enable interrupts */
        bl  schedule
    recheck:
        LOAD_MSR_KERNEL(r10,MSR_KERNEL)
        SYNC
        MTMSRD(r10)     /* disable interrupts */
        rlwinm  r9,r1,0,0,(31-THREAD_SHIFT)
        lwz r9,TI_FLAGS(r9)
        andi.   r0,r9,_TIF_NEED_RESCHED
        bne-    do_reschedandi.   r0,r9,_TIF_SIGPENDING
        beq restore_user
    do_user_signal:         /* r10 contains MSR_KERNEL here */


    asmlinkage void __sched preempt_schedule_irq(void){
        struct thread_info *ti = current_thread_info();
        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);
            barrier();
        } while (unlikely(test_thread_flag(TIF_NEED_RESCHED)));
    }

    asmlinkage void __sched preempt_schedule(void){
        struct thread_info *ti = current_thread_info();
        //preempt_cout非0的话,就不调用schedule
        if (likely(ti->preempt_count || irqs_disabled()))
            return;

        do {
            add_preempt_count(PREEMPT_ACTIVE);
            schedule();
            sub_preempt_count(PREEMPT_ACTIVE);
            barrier();
        } while (unlikely(test_thread_flag(TIF_NEED_RESCHED)));
    }
    #########################################################################################;
     内核中的执行路径主要有:
     1  用户进程的内核态,此时有进程context,主要是代表进程在执行系统调用等。
        还包括,内核中自己的进程,如 ksoftirqd 等等
     2  中断或者异常或者自陷等,从概念上说,此时没有进程context,不能进行context switch。
     3  bottom_half,从概念上说,此时也没有进程context。
     4  同时,相同的执行路径还可能在其他的CPU上运行。

    Linux2.6中网络代码中的preempt_enable/disable移到softirqd调用的地方原因是这样的.
    一、部分softirq是isr处理之后调用的,
        对于这部分代码,由于是在底半处理中运行,必须是是在运行进程系统调用之前返回的.
        所以实际上preempt_disable(); preempt_enable();代码对于他们来说是没有意义的.
    二、部分softirq是在ksoftirqd的内核线程运行的,
        因为这个相当于运行在进程的内核空间,由于软中断都是对中断上半部的继续,
        所以这些工作都需要尽快的完成.所以在softirqd运行的时候,禁止了preempt,
        这样就可以保证softirq运行完之后才会调度下一个进程,因为softirq里面的所有函数都不会睡眠.

  • 相关阅读:
    Git------解决右键不显示Git Bash Here问题
    AngularJS------Error: Cannot find module '@angular-devkit/core'
    AngularJS------命令行
    AngularJS------报错"The selector "app-user-item" did not match any elements"
    AngularJS------使用VSCode创建的Angular项目部署IIS
    JQuery------各种版本下载
    docker 卸载
    oauth2.0授权协议
    web service 简介
    Python 优雅获取本机 IP 方法
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/5391887.html
Copyright © 2020-2023  润新知