实时操作系统与非实时操作系统
在工控领域,我们经常听到实时系统和非实时系统,Linux、Windows是典型的非实时系统,典型的实时操作系统有VxWorks,RT-Thread,uCOS,QNX,WinCE等。
“实时、非实时系统”和“抢占、非抢占调度”的关系
抢占式调度:优先级高的进程可以抢占CPU执行,在这种调度算法中,系统总是选择优先级别的最高的算法进行调度,并且 一旦高优先级别的任务准备就绪之后,它就会马上被调度而不等待低优先级的任务主动放弃CPU。
非抢占式调度:一般指轮询式的调度,当一个进程已经处于内核态,即已经产生了内部的系统调用,除非自愿放弃CPU,否则该进程将会一直运行下去,直至完成或退出内核。
在 RTOS中,主要的调度算法是基于任务优先级的抢占式调度,如果不使用抢占式调度,就无法保证在规定时间内完成工作,因为无法确认当前占有CPU的进程什么时候退出CPU。 所以实时系统和抢占式调度是相互依存的关系,没有抢占式调度,就没有实时系统。(暂下这么一个结论,其他实现实时系统的方式暂未考证)
Linux的preempt抢占机制
当前的Linux内核加入了内核抢占(preempt)机制。内核抢占指用户程序在执行系统调用期间可以被抢占,该进程暂时挂起,使新唤醒的高优先 级进程能够运行。这种抢占并非可以在内核中任意位置都能安全进行,比如在临界区中的代码就不能发生抢占。临界区是指同一时间内不可以有超过一个进程在其中 执行的指令序列。在Linux内核中这些部分需要用自旋锁保护。
如何避免进程被抢占(自旋锁)
自旋锁怎么用
前边了解到Linux中有些地方是不可以进行抢占的,我们要限制一些在临界区的抢占,我们应该利用自旋锁进行保护。 自旋锁的用法教程非常多,这里不再赘述。 参考文献:https://blog.csdn.net/silent123go/article/details/52760424
当执行了我们执行了锁资源之后,程序将执行preempt_disable(),代码如下。
#define _spin_lock(lock) \
do { \
preempt_disable(); \
_raw_spin_lock(lock); \
__acquire(lock); \
} while(0)
判断是否可抢占的条件(未完成、待补充)
void preempt_count_add(int val) 用于增加当前进程的引用计数,这样可以避免当前进程被抢占 与之对应的是void preempt_count_sub(int val) 用来当前进程的引用计数,这样当引用计数为0时,当前进程就可以被抢占. 这两个函数是一对的,一般一起使用 其使用的例程如下:
#define __irq_enter() \
do { \
account_irq_enter_time(current); \
preempt_count_add(HARDIRQ_OFFSET); \
trace_hardirq_enter(); \
} while (0)
#define __irq_exit() \
do { \
trace_hardirq_exit(); \
account_irq_exit_time(current); \
preempt_count_sub(HARDIRQ_OFFSET); \
} while (0)
可以看到在进入irq是调用preempt_count_add 来增加引用计数避免被抢占,离开irq是调用preempt_count_sub 来减少引用计数使能抢占 其源码分析如下:
void preempt_count_add(int val)
{
#ifdef CONFIG_DEBUG_PREEMPT
if (DEBUG_LOCKS_WARN_ON((preempt_count() < 0)))
return;
#endif
__preempt_count_add(val);
#ifdef CONFIG_DEBUG_PREEMPT
DEBUG_LOCKS_WARN_ON((preempt_count() & PREEMPT_MASK) >=
PREEMPT_MASK - 10);
#endif
preempt_latency_start(val);
}
假定不打开CONFIG_DEBUG_PREEMPT的话,则preempt_count_add 中首先调用__preempt_count_add 来增加引用计数,然后调用preempt_latency_start 来开始 Start timing the latency.这个has如果没有定义CONFIG_DEBUG_PREEMPT 和 CONFIG_PREEMPT_TRACER的话,也等同于空函数.
void preempt_count_sub(int val)
{
#ifdef CONFIG_DEBUG_PREEMPT
if (DEBUG_LOCKS_WARN_ON(val > preempt_count()))
return;
if (DEBUG_LOCKS_WARN_ON((val < PREEMPT_MASK) &&
!(preempt_count() & PREEMPT_MASK)))
return;
#endif
preempt_latency_stop(val);
__preempt_count_sub(val);
}
假定不打开CONFIG_DEBUG_PREEMPT的话,则ppreempt_count_sub 中首先调用preempt_latency_stop 来Stop timing the latency来增加引用计数,然后调用preempt_latency_start 来开始 然后调用__preempt_count_sub 来减少当前进程的引用计数,如果引用计数是0的话,则使能进程抢占.
参考文献:http://blog.sina.com.cn/s/blog_502c8cc401012pxj.html
|