• 实时抢占补丁概观(待续)


    A realtime preemption overview(2005-08-10/Paul McKenney)
    实时抢占补丁概观

    Yang Honggang<eagle.rtlinux@gmail.com>
    ref: http://lwn.net/Articles/146861/
    ----------------------------------------

    ////PREEMPT_RT的思想

    PREEMPT_RT补丁的核心是最小化(Linux)内核中不可抢占部分的代码,同时又将
    为支持抢占性必须要修改的代码量最小化。

    临界区、中断处理函数、关中断等代码序列通常是进行抢占改进的。

    PREEMPT_RT补丁利用Linux内核的SMP特性来进行可抢占改进,这样就避免了对
    重写整个Linux代码。

    从某种程度上讲,我们可以简单地认为抢占是在系统中添加了一个新的CPU,
    然后利用通常的锁机制来对抢占任务进行同步。

    注意不要对上面的叙述死扣,比如在每次抢占时PREEMPT_RT并没有产生
    CPU热插拔事件。关键是底层SMP环境必须提供容忍自由抢占的机制。
    下面的章节给出来PREEMPT_RT的思想是怎样实施的。

    ////PREEMPT_RT特性

    1. 临界区可抢占
    2. 中断处理函数可抢占
    3. "关中断"代码序列可抢占
    4. 内核中的spinlock和semaphore支持优先级继承
    5. 延迟操作
    6. 降低延迟的措施

    下面分别介绍:

    /// 1. 临界区可抢占

    在PREEMPT-RT中通常的spinlock(spinlock_t和rwlock_t)、RCU“读部分”的临界区
    (rcu_read_lock()和rcu_read_unlock())都是可抢占的。
    Semaphore临界区也是可以抢占的(没有打PREEMPT_RT补丁普通内核也是这样的)。
    该可抢占性意味着当获取 spinlock时也可以阻塞,反过来,当关闭中断或者抢占时,
    不应该去申请spinlock。这也意味着spinlock_t中使用的spin_lock_irqsave()没有
    关闭硬件中断。

        测试#1: 在普通内核中怎样支持semaphore临界区抢占?

    那么在中断或者抢占关闭的条件下需要申请锁时该如何去做?可以使用raw_spinlock_t
    而不是spinlock_t。在raw_spinlock_t中会调用spin_lock()。
    PREEMPT_RT中引入了一系列的宏让spin_lock()表现的像C++中的重载一样。当在raw_spinlock_t
    中调用时,它表现为传统的spinlock,当在spinlock_t中调用时,它的临界区又可被抢占。
    例如,多种_irq原语(如spin_lock_irqsave())用在raw_spinlock_t中时,会关闭硬件中断。
    但是,用在spinlock_t中时却不会关闭硬件中断。然而,对于raw_spinlock_t(以及相应的rwlock_t
    、raw_rwlock_t)的使用不遵循该规则。在调度器、平台相关的代码和RCU等很少的底层部分才
    需要这些raw lock,其他地方用不到。

    因为临界区可以被抢占,那么不能指望给定的临界区仅在一个固定的CPU上执行。由于抢占的原因,
    它可能会迁移到另一个不同的CPU上运行。这样,当在临界区中使用per-CPU变量时,就需要
    单独的处理可能发生的抢占带来的问题。因为spinlock_t和rwlock_t不会处理这些事情。
    可行的方法有:
     1. 通过get_cpu_var()、preempt_disable()或者关闭硬件中断等显式地关闭抢占。
     2. 使用per-CPU锁保护per-CPU变量。一种方法是使用DEFINE_PER_CPU_LOCKED(),之后会更多
        的介绍。

    因为现在spin_lock()可以睡眠,那么需要增加额外的任务状态。看下Ingo Molnar提供的
    代码片段:

        spin_lock(&mylock1);
        current->state = TASK_UNINTERRUPTIBLE;
        spin_lock(&mylock2);                    // [*]
        blah();
        spin_unlock(&mylock2);
        spin_unlock(&mylock1);

    由于[*]处的spin_lock可能会睡眠,这样就会破坏current->state的值。这对blah()来说是不应该
    发生的。因此,引入了TASK_RUNNING_MUTEX位来告知调度器在调度前对之前的current->state值进行
    保护。虽然这么做有些怪怪的,但是这么做确实可以在代码修改量最小的前提下支持临界区抢占。
    并且,这么做使得同样的代码可以在PREEMPT_RT, PREEMPT和non-PREEMPT配置下工作。



  • 相关阅读:
    225. 用队列实现栈
    415. 字符串相加
    rabbitmq的基本使用
    3. 无重复字符的最长子串
    面试题59
    面试题30. 包含min函数的栈
    面试题09. 用两个栈实现队列
    287. 寻找重复数
    1137. 第 N 个泰波那契数
    70. 爬楼梯
  • 原文地址:https://www.cnblogs.com/pangblog/p/3278509.html
Copyright © 2020-2023  润新知