加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

Linux实时PREEMPT_RT补丁

(2020-06-19 13:05:24)
标签:

linux

kernel-programming

rt

杂谈

分类: programming
PREEMPT_RT补丁可以把通用Linux转换为具有类似VxWorks实时功能的操作系统,主要的改动包括:

1. local_irq_save vs local_irq_save_nort(local_irq_restore vs local_irq_restore_nort)
local_irq_save定义为:
===========================
#define local_irq_save(flags)                \
    do {                        \
        raw_local_irq_save(flags);        \
        trace_hardirqs_off();            \
    } while (0)
#define local_irq_restore(flags)            \
    do {                        \
        if (raw_irqs_disabled_flags(flags)) {    \
            raw_local_irq_restore(flags);    \
            trace_hardirqs_off();        \
        } else {                \
            trace_hardirqs_on();        \
            raw_local_irq_restore(flags);    \
                          \
    } while (0)
=========================
#define raw_local_irq_save(flags)            \
    do {                        \
        typecheck(unsigned long, flags);    \
        flags = arch_local_irq_save();        \
    } while (0)
#define raw_local_irq_restore(flags)            \
    do {                        \
        typecheck(unsigned long, flags);    \
        arch_local_irq_restore(flags);        \
    } while (0)
===============================
local_irq_save_nort的定义:
======================================
# define local_irq_save_nort(flags)   \
      do { local_save_flags(flags); } while (0)

#define local_save_flags(flags)                \
    do {                        \
        raw_local_save_flags(flags);        \
    } while (0)

#define raw_local_save_flags(flags)            \
    do {                        \
        typecheck(unsigned long, flags);    \
        flags = arch_local_save_flags();    \
    } while (0)
===================================
对比最后调用了不同的函数,对应arm架构的最终代码如下,差别为实时版本只保存了cpsr寄存器,普通版本的要关中断。
----------------------------------------
flags = arch_local_irq_save(); 
static inline unsigned long arch_local_irq_save(void)
{
    unsigned long flags;

    asm volatile(
          mrs    %0, cpsr    @ arch_local_irq_save\n"
          cpsid    i"
        : "=r" (flags) : : "memory", "cc");
    return flags;
}
------------------------------------------
flags = arch_local_save_flags();
------------------------------------------
static inline unsigned long arch_local_save_flags(void)
{
    unsigned long flags;
    asm volatile(
          mrs    %0, cpsr    @ local_save_flags"
        : "=r" (flags) : : "memory", "cc");
    return flags;
}
-----------------------------------------------
稍微解析一下gcc 内联汇编代码,gcc 内联汇编代码的格式为
--------------------------------------

asm [volatile] (Assembly code
                : OutputOperands 
                : InputOperands  
                : Clobbers       
                )
----------------------------------------
在arch_local_irq_save代码中,第一部分 ”mrs    %0, cpsr“,把cpsr寄存器值存入到第一个参数,即flags中, "    cpsid    i"关中断; : "=r" (flags) : 输出寄存器列表,%0即指代flags变量,没有输入寄存器,所以是为空,但::需要保留,"=r"称为限制字符串,在arm中,"=r"表示通用寄存器;clobber list表示一列寄存器名和其他修饰符,例如"r1", "r4", "cc" "esp", "memory",cc指示编译器汇编代码更新了条件寄存器CPSR,memory指示编译器汇编执行了读写内存操作,编译器需要flush寄存器到内存。

2. disable_irq vs disable_irq_nosync
============================
disable_irq的定义
-----------------------------
void disable_irq(unsigned int irq)
{
    if (!__disable_irq_nosync(irq))
        synchronize_irq(irq);
}
×××××××××××××××××××××××××××××××
disable_irq_nosync
---------------------------
void disable_irq_nosync(unsigned int irq)
{
    __disable_irq_nosync(irq);
}
============================
实时patch中用disable_irq_nosync代替disable_irq,差别就是去除了等待irq handler完成的操作。

3.  wake_up_all vs wake_up_all_locked, 实时版本的没有关中断操作
================================
wake_up_all_locked实现
------------------------------
#define wake_up_all_locked(x)        __wake_up_locked((x), TASK_NORMAL, 0)

void __wake_up_locked(wait_queue_head_t *q, unsigned int mode, int nr)
{
    __wake_up_common(q, mode, nr, 0, NULL);
}
static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,
            int nr_exclusive, int wake_flags, void *key)
{
    wait_queue_t *curr, *next;

    list_for_each_entry_safe(curr, next, &q->task_list, task_list) {
        unsigned flags = curr->flags;

        if (curr->func(curr, mode, wake_flags, key) &&
                (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
            break;
    }
}
----------------------------------
wake_up_all实现
--------------------------------
#define wake_up_all(x)            __wake_up(x, TASK_NORMAL, 0, NULL)

void __wake_up(wait_queue_head_t *q, unsigned int mode,
            int nr_exclusive, void *key)
{
    unsigned long flags;

    spin_lock_irqsave(&q->lock, flags);
    __wake_up_common(q, mode, nr_exclusive, 0, key);
    spin_unlock_irqrestore(&q->lock, flags);
}
--------------------------------------------
可见,实时patch在wakeup进程时没有关中断,正常版本调用了spin_lock_irqsave关中断。

4.   spin_lock  vs spin_lock_irqsave  在驱动中,spin_lock被spin_lock_irqsave替换, spin_lock不能用于中断处理函数中,spin_lock_irqsave在多核处理器嵌套中断中使用也没问题.
-----------------------------------------------
spin_lock 定义
×××××××××××××××××××××××××××××
spin_lock  ----->  raw_spin_lock -->do_raw_spin_lock->arch_spin_lock(&lock->raw_lock);
static inline void __raw_spin_lock(raw_spinlock_t *lock)
{
        preempt_disable();
        spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
        LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
}
以arm为例,arch_spin_lock定义
static inline void arch_spin_lock(arch_spinlock_t *lock)
{
    unsigned long tmp;
    u32 newval;
    arch_spinlock_t lockval;

    __asm__ __volatile__(
"1:    ldrex    %0, [%3]\n"
   add    %1, %0, %4\n"
   strex    %2, %1, [%3]\n"
   teq    %2, #0\n"
   bne    1b"
    : "=&r" (lockval), "=&r" (newval), "=&r" (tmp)
    : "r" (&lock->slock), "I" (1 << TICKET_SHIFT)
    : "cc");

    while (lockval.tickets.next != lockval.tickets.owner) {
        wfe();
        lockval.tickets.owner = ACCESS_ONCE(lock->tickets.owner);
    }

    smp_mb();
}
---------------------------------------------------------------------
spin_lock_irqsave定义
×××××××××××××××××××××××××××××
spin_lock_irqsave-->raw_spin_lock_irqsave->_raw_spin_lock_irqsave->__raw_spin_lock_irqsave
static inline unsigned long __raw_spin_lock_irqsave(raw_spinlock_t *lock)
{
    unsigned long flags;

    local_irq_save(flags);
    preempt_disable();
    spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
    do_raw_spin_lock_flags(lock, &flags); -->arch_spin_lock // ARM arch
    return flags;
}
保存中断状态寄存器状态,关中断,禁止内核抢占,和spin_lock相比多了个保存中断和关中断指令
-----------------------------------------------

5. cpu_relax vs cpu_chill, RT 版本用cpu_chill 替换cpu_relax
==========================================
cpu_chill 在实时版本用nanosleep实现了一个1ms的时延。而在非实时版本等同cpu_relax,这样被抢夺的task
可以获得运行机会。
cpu_relax, 在aarch64 arm系统中实现如下,memory指示CPU内存发生了变化,yield指示当前进程可以暂停被交换出去,而让其他进程运行。
static inline void cpu_relax(void)
{
    asm volatile("yield" ::: "memory");
}



0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有