转自: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里面的所有函数都不会睡眠.

内核抢占实现(preempt) 【转】的更多相关文章

  1. Linux内核抢占与中断返回【转】

    转自:http://blog.csdn.net/tommy_wxie/article/details/7425728 版权声明:本文为博主原创文章,未经博主允许不得转载. [html] view pl ...

  2. Linux用户抢占和内核抢占详解(概念, 实现和触发时机)--Linux进程的管理与调度(二十)

    1 非抢占式和可抢占式内核 为了简化问题,我使用嵌入式实时系统uC/OS作为例子 首先要指出的是,uC/OS只有内核态,没有用户态,这和Linux不一样 多任务系统中, 内核负责管理各个任务, 或者说 ...

  3. Linux内核抢占实现机制分析【转】

    Linux内核抢占实现机制分析 转自:http://blog.chinaunix.net/uid-24227137-id-3050754.html [摘要]本文详解了Linux内核抢占实现机制.首先介 ...

  4. Linux下的内核抢占

    2017-03-03 很遗憾之前在介绍进程调度的文章中,虽然涉及到了内核抢占,但是却没有对其进行深入介绍,今天就稍微总结下内核抢占. 内核抢占在一定程度上减少了对某种事件的响应延迟,这也是内核抢占被引 ...

  5. Linux2.6内核--抢占

    [摘要]本文首先介绍非抢占式内核(Non-Preemptive Kernel)和可抢占式内核(Preemptive Kernel)的区别.接着分析Linux下有两种抢占:用户态抢占(User Pree ...

  6. 20169212《Linux内核原理与分析》第七周作业

    实验 给MenuOS增加time和time-asm命令的方法: 更新menu代码到最新版 再main()函数中增加MenuConfig 增加对应的Time函数和TimeAsm函数(这里的函数要换成我们 ...

  7. MIT6.828 Lab4 Preemptive Multitasking(下)

    Lab4 Preemptive Multitasking(下) lab4的第二部分要求我们实现fork的cow.在整个lab的第一部分我们实现了对多cpu的支持和再多系统环境中的切换,但是最后分析的时 ...

  8. 抢占式内核与非抢占式内核中的自旋锁(spinlock)的差别

    一.概括 (1)自旋锁适用于SMP系统,UP系统用spinlock是作死. (2)保护模式下禁止内核抢占的方法:1.运行终端服务例程时2.运行软中断和tasklet时3.设置本地CPU计数器preem ...

  9. Linux内核原子(1) - spinlock的实现

    spinlock的数据结构spinlock_t定义在头文件linux/spinlock_types.h里面: typedef struct { raw_spinlock_t raw_lock; #if ...

随机推荐

  1. 单选 name的值相同时候 就会产生互斥现象

  2. BZOJ 1930 吃豆豆(费用流)

    首先这题的两条线不相交的限制可以去掉,因为如果相交的话把点换一换是不影响最终结果的. 剩下的费用流建图是显然的,把点拆为两个,建立超级源点s和源点ss汇点t,连边(s,ss,2,0). 对于每个点,连 ...

  3. 【刷题】BZOJ 1001 [BeiJing2006]狼抓兔子

    Description 现在小朋友们最喜欢的"喜羊羊与灰太狼",话说灰太狼抓羊不到,但抓兔子还是比较在行的,而且现在的兔子还比较笨,它们只有两个窝,现在你做为狼王,面对下面这样一个 ...

  4. [洛谷4609] [FJOI2016]建筑师

    题目描述 LOJ题面:https://loj.ac/problem/2173. 洛谷题面:https://www.luogu.org/problemnew/show/P4609. Solution [ ...

  5. [JLOI2014]松鼠的新家 树上差分

    差分 一开始竟然想分情况讨论来差分,然后发现各自情况要分析, 就是为了解决中间节点重复计算的问题, 结果 最后一想,中间重复计算了一次,那我最后减掉不就好了么,,, 那这就是一道差分裸题了(这是唯一不 ...

  6. POJ2286:The Rotation Game——题解

    http://poj.org/problem?id=2286 题目大意:如图所示有一种玩具,每次可以拉动A-H的开关使得整个行(或列)向字母方向移动一位(如果移动到头的话则到行(列)尾部) 求使得中间 ...

  7. some of the properties associated with the solution could not be read解决方法

    基于TFS管理的解决方案打开时提示:“some of the properties associated with the solution could not be read”,并不影响项目加载,O ...

  8. 如何给apk文件签名

    1.签名的意义 为了保证每个应用程序开发商合法ID,防止部分开放商可能通过使用相同的Package Name来混淆替换已经安装的程序,我们需要对我们发布的APK文件进行唯一签名,保证我们每次发布的版本 ...

  9. PhoneGap API介绍:Camera

    本文将介绍PhoneGap API——Camera:使用设备的摄像头采集照片,对象提供对设备默认摄像头应用程序的访问. 方法: camera.getPicture 参数: cameraSuccess ...

  10. 使用 Rational AppScan 保证 Web 应用的安全性,第 1 部分: Web 安全与 Rational AppScan 入门

    前言 当今世界,Internet(因特网)已经成为一个非常重要的基础平台,很多企业都将应用架设在该平台上,为客户提供更为方便.快捷的服务支持.这些应用 在功能和性能上,都在不断的完善和提高,然而在非常 ...