1. 操作系统的实时性

        核心在于,内核是否可以无条件的被中断被抢占。

        真正的实时操作系统内核是可中断可抢占的。而非实时的操作系统通常在执行内核功能时是不可中断的,Linux是软实时的,它在内核中加入了若干可中断点,而不是任何时候都允许中断

        Windows的内核是不可抢占的,所以它不是实时操作系统。Linux是软实时的,内核中仍然有大量不可抢占区域。

        window和linux大多认为是分时操作系统,可以多账户运行,有固定的时间片。

关于内核的抢占

 在Linux2.6中,内核己经可以抢占,因而实时性得到了加强"但是内核中仍有大量的不可抢占区域,如由自旋锁(SPinlock)保护的临界区。

关于时钟粗粒度

Linux2.6内核虽然把时钟频率提高到1000 赫兹,定时精度达到了1ms,但远不能满足实时系统要求的微秒级定时精度,如数控系统要求50us 的定时精度。

关于中断:

系统调用和中断服务程序中,为了保护临界区资源,Linux会长时间关闭中断"有些系统调用和中断服务程序的时间还很长,这样会加大中断延迟时间。

关于任务调度算法

Linux系统是按照分时系统的目标设计的,以达到系统较好的平均性能,强调平衡各进程之间的响应时间来保证公平的CPU时间占用。通常采用固定时间片的分时调度算法,内核不能抢占,而实时系统的行为更多的取决于复杂的不可预知的情况。这些原则不能满足实时系统短的响应时间和确定的执行行为的要求。

关于优先级反转问题:

当一个低优先级的进程占用了某种资源,导致同样需要这个资源的高级进程无法运行,并且此时有一个优先级在他们之间的就绪进程获得了CPU 的控制权,这样就使得高级别的任务需要等待比他优先级别低的任务,这种现象就叫做优先级反转。在Linux中,由于资源是不可抢占的,并且不支持优先级继承等策略,所以优先级反转现象可能会发生,这影响了系统的实时性能。


改进linux实时性的方案:

1. 直接修改内核法:

通过对Linux 原理与源代码的分析,通过对内核的进程调度、中断服务程序等代码进行修改与优化,提高系统的实时性能,并且为了保证系统的通用性,需要按照POSIX的相关标准来进行改动。利用这种方法可以获得较好的性能提升,并且因为与标准的Linux 内核使用相同的应用程序接口,所以有良好的兼容性。目前,采用修改内核方法改进Linux 实时性的产品很多,如 Kurt-Linux、TimesysLinux 和 Ingo's RT patch 等

2. 双内核法:

通过在Linux 内核与硬件中断之间增加一个可抢先的实时内核,把标准的Linux 内核作为该实时内核的一个优先级最低的进程来调度,它可以被实时进程抢断,正常的Linux 进程仍可以在 Linux 内核上运行,这样既可以使用标准分时操作系统即Linux 的各种服务,又能提供低延时的实时环境。RT-Linux 是采用双内核法改造Linux 实时性的典型代表。



                                                                                                               linux下的抢占


1. 用户抢占

一般来说,用户抢占发生几下情况:

从系统调用返回用户空间;

从中断(异常)处理程序返回用户空间

从这里我们可以看到, 用户抢占是发生在用户空间的抢占现象.

更详细的触发条件如下所示, 其实不外乎就是前面所说的两种情况: 从系统调用或者中断返回用户空间

时钟中断处理例程检查当前任务的时间片,当任务的时间片消耗完时,scheduler_tick()函数就会设置need_resched标志;

信号量、等到队列、completion等机制唤醒时都是基于waitqueue的,而waitqueue的唤醒函数为default_wake_function,其调用try_to_wake_up将被唤醒的任务更改为就绪状态并设置need_resched标志。

设置用户进程的nice值时,可能会使高优先级的任务进入就绪状态;

改变任务的优先级时,可能会使高优先级的任务进入就绪状态;

新建一个任务时,可能会使高优先级的任务进入就绪状态;

对CPU(SMP)进行负载均衡时,当前任务可能需要放到另外一个CPU上运行

2. 内核抢占

如果内核处于相对耗时的操作中,就会导致系统的延迟增加。

要满足什么条件,kernel才可以抢占一个任务的内核态呢?

没持有锁。锁是用于保护临界区的,不能被抢占。

Kernel code可重入(reentrant)。因为kernel是SMP-safe的,所以满足可重入性。

内核抢占发生的时机,一般发生在:

当从中断处理程序正在执行,且返回内核空间之前。当一个中断处理例程退出,在返回到内核态时(kernel-space)。这是隐式的调用schedule()函数,当前任务没有主动放弃CPU使用权,而是被剥夺了CPU使用权。

当内核代码再一次具有可抢占性的时候,如解锁(spin_unlock_bh)及使能软中断(local_bh_enable)等, 此时当kernel code从不可抢占状态变为可抢占状态时(preemptible again)。也就是preempt_count从正整数变为0时。这也是隐式的调用schedule()函数

如果内核中的任务显式的调用schedule(), 任务主动放弃CPU使用权

如果内核中的任务阻塞(这同样也会导致调用schedule()), 导致需要调用schedule()函数。任务主动放弃CPU使用权

内核抢占,并不是在任何一个地方都可以发生,以下情况不能发生

内核正进行中断处理。在Linux内核中进程不能抢占中断(中断只能被其他中断中止、抢占,进程不能中止、抢占中断),在中断例程中不允许进行进程调度。进程调度函数schedule()会对此作出判断,如果是在中断中调用,会打印出错信息。

内核正在进行中断上下文的Bottom Half(中断下半部,即软中断)处理。硬件中断返回前会执行软中断,此时仍然处于中断上下文中。如果此时正在执行其它软中断,则不再执行该软中断。

内核的代码段正持有spinlock自旋锁、writelock/readlock读写锁等锁,处干这些锁的保护状态中。内核中的这些锁是为了在SMP系统中短时间内保证不同CPU上运行的进程并发执行的正确性。当持有这些锁时,内核不应该被抢占。

内核正在执行调度程序Scheduler。抢占的原因就是为了进行新的调度,没有理由将调度程序抢占掉再运行调度程序。

内核正在对每个CPU“私有”的数据结构操作(Per-CPU date structures)。在SMP中,对于per-CPU数据结构未用spinlocks保护,因为这些数据结构隐含地被保护了(不同的CPU有不一样的per-CPU数据,其他CPU上运行的进程不会用到另一个CPU的per-CPU数据)。但是如果允许抢占,但一个进程被抢占后重新调度,有可能调度到其他的CPU上去,这时定义的Per-CPU变量就会有问题,这时应禁抢占。

你可能感兴趣的:(1. 操作系统的实时性)