Linux实时PREEMPT_RT补丁
(2020-06-19 13:05:24)
标签:
linuxkernel-programmingrt杂谈 |
分类: 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)
===============================
#define raw_local_irq_restore(flags)
===============================
local_irq_save_nort的定义:
======================================
# define
local_irq_save_nort(flags)
\
#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->
static inline void __raw_spin_lock(raw_spinlock_t *lock)arch_spin_lock(&lock->raw_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)
{
}