zoukankan      html  css  js  c++  java
  • Linux下的内核抢占

    2017-03-03


    很遗憾之前在介绍进程调度的文章中,虽然涉及到了内核抢占,但是却没有对其进行深入介绍,今天就稍微总结下内核抢占。

      内核抢占在一定程度上减少了对某种事件的响应延迟,这也是内核抢占被引入的目的。之前的内核中,除了显示调用系统调度器的某些点,内核其他地方是不允许中重新调度的,如果内核在做一些比较复杂的工作,就会造成某些急于处理的事得不到及时响应。针对内核抢占其实本质上也是对当前进程而言(不知道这么描述是否合适),因为内核是为用户程序提供服务,换言之,其本身不会主动的去执行某个动作。这里内核抢占,重点在于用户程序请求内核服务时,CPU切换到内核态在执行某个系统服务期间,被抢占。

      当然,即使支持内核抢占,也不是什么时候都可以的,还是要考虑对临界区的保护。类似于多处理器架构,如果进程A陷入到内核模式访问某个临界资源,而在访问期间,进程B也要访问临界区,如果这种抢占被允许,那么就发生了临界区被重入。所以,在访问临界资源时需要禁止内核抢占,在出临界区则要开启内核抢占。

      为了支持内核抢占,在进程结构体的thread_info结构中有个preempt_count字段,用以记录当前内核(活动)是否可以被抢占。当该值为0时,允许被抢占;否则,不允许。

      关于内核抢占有几个函数:
      

    #define preempt_disable() 
    do { 
        inc_preempt_count(); 
        barrier(); 
    } while (0)
    
    
    #define preempt_enable() 
    do { 
        preempt_enable_no_resched(); 
        barrier(); 
        preempt_check_resched(); 
    } 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)

    上面两个函数是禁止和启用内核抢占。其实就是一个宏定义。禁止内核抢占本质上就是把当前进程的thread_info结构中的preempt_count字段加1,而启用内核抢占就是减1.注意这里启用内核抢占之后,调用了preempt_check_resched检查当前是否需要重新调度,这也是一个宏,实现如下:

    #define preempt_check_resched() 
    do { 
        if (unlikely(test_thread_flag(TIF_NEED_RESCHED))) 
            preempt_schedule(); 
    } while (0)

     就是在开启内核抢占之后,检查下此时是否有比较重要的进程等待执行,如果有,则调用preempt_schedule函数执行调度,可见在显示开启内核抢占之后,是触发内核抢占的一个时机。而调度过程本身是不允许被抢占的。preempt_schedule函数如下

    asmlinkage void __sched notrace preempt_schedule(void)
    {
        struct thread_info *ti = current_thread_info();
    
        /*
         * If there is a non-zero preempt_count or interrupts are disabled,
         * we do not want to preempt the current task. Just return..
         */
        if (likely(ti->preempt_count || irqs_disabled()))
            return;
    
        do {
            add_preempt_count_notrace(PREEMPT_ACTIVE);
            __schedule();
            sub_preempt_count_notrace(PREEMPT_ACTIVE);
    
            /*
             * Check again in case we missed a preemption opportunity
             * between schedule and now.
             */
            barrier();
        } while (need_resched());
    }

    如果抢占计数器不为0或者当前处于关闭硬件中断状态均是不可以被抢占的。

    前面提到,在不支持内核抢占的内核中,内核态程序会一直执行,直到返回用户空间进程时,才会检查调度。在内核中执行期间,能打断当前执行的,只有中断,在硬件中断来了之后,处理器会根据情况着手处理硬件中断,在硬件中断处理完毕需要恢复现场时,若检查之前的状态是内核态,则不触发调度,只有之前状态是用户态时才会触发调度。而在支持内核抢占的内核中,在从硬件中断返回时,不管是返回用户态和内核态都会检查调度,若是返回内核态,检查当前线程调度标识和抢占标识,若都允许,则可进程调度。这是内核抢占下由增加的一个调度点。

     参考资料:

    1、linux 3.10.1内核

    2、深入linux内核架构

  • 相关阅读:
    (二)Vue + Element 案例
    npm使用快速的安装源(nrm)
    windows下安装pm2
    windows下Mongodb和Memcached安装笔记
    利用UnblockNeteaseMusic实现网易云破版权,并实现shell可快速访问
    mac下相关操作命令
    apache下设置域名多站点访问及禁止apache访问80端口
    判断密码是否含有字母数字
    隐藏手机号
    开发相关工具
  • 原文地址:https://www.cnblogs.com/ck1020/p/6497963.html
Copyright © 2011-2022 走看看