OS之进程管理---实时CPU调度
引言
一般来说,我们将实时操作系统区分为软实时系统(soft real-time system)和硬实时系统(hard real-time system)。软实时系统不保证会调度关键实时进程,而只保证这类进程会优先于非关键进程。硬实时系统具有更严格的要求:一个任务应在它的截止期限之前完成,在截止期限之后完成与没有完成是一样的。
最小化延迟
从事件到事件得到服务的这段时间称为事件延迟。事件延迟有两种类型:
-
中断延迟
中断延迟是从CPU收到中断到中断程序开始的时间。当一个中断发生时,操作系统应先完成正在执行的指令,在确定发生中断的类型,然后应该保存当前进程的执行状态,再采用特定的中断服务程序(ISR)来处理中断。执行上面说的这些任务所需要的时间就是中断延迟。 -
调度延迟
调度程序从停止一个进程到启动另外一个进程所需的时间量称为调度延迟。提供实时任务立即访问CPU要求,实时操作系统最大限度的减少这种延迟。保持调度延迟尽可能低的最有效的技术就是提供抢占式内核。
优先权调度
我们知道实时系统最重要的功能就是能够保证实时进程的相应。所以实时系统的地调度程序应支持抢占的基于优先权的算法。比如window提供32个不同的优先级,优先级的值16-31,专门用于实时系统,在Linux、Solaris中也具有类似的优先级方案
注意:提供抢占式的、基于优先级的调度程序仅能够保证软实时功能。硬实时系统应该进一步保证实时任务应在截止期限内得到服务。
首先进行调度的程序是周期性的,也就是说这些程序定期需要CPU,一旦周期性的获得CPU,他们具有固定的处理时间t、CPU应处理的截止期限d、以及周期p,三者的关系应为:0tdp。周期任务的速率是。这样调度程序就利用这些特性,根据进程的截止期限或速率要求分配优先级。
这种形式调度特点在于:进程可能应向调度器公布截止期限要求。然后使用一种称为准入控制算法的技术,调度程序做两件事:他承认进程,保证进程完成;如果不能保证任务在截止期限前得到完成,拒绝请求。
单调速率调度
单调速率调度算法采用抢占式的、静态优先级的策略,调度周期性任务。所有的周期性任务在进入系统时会分配一个优先级,其值与周期成反比,即:周期越短,优先级越高;周期越长,优先级越低。这种算法假定:对于每次CPUU执行,周期性进程的处理时间是相同的,也就是在每次进程获取CPU时,它的CPU执行长度相同的。
单调速率调度可认为是最优的,如果一组进程不能由此算法调度,他不能由任何其他分配静态优先级的算法来执行。
尽管单调速率调度是最优的,但是有一个限制:CPU的利用率是有限的,并步总是可能完全最大化CPU资源。调度N个进程的最坏情况下的CPU利用率为:
N( - 1)
对于具有一个进程的资源,CPU的利用率是100%。但是当进程数量趋近无穷时,它大约接近69%;对于具有两个进程的系统,CPU利用率在83%左右。
最早截止期限有限优先调度
最早截止期限有限调度(EDF)调度根据截止期限动态的分配有限级。截止期限越早,优先级越高,截止期限越晚,优先级越低。根据EDF策略:当一个进程可运行时。它要要系统公布截止期限要求。优先级可能需要进行调整,以便反应新可运行进程的截止期限(对比单调速率调度,其优先级是固定的)。
EDF调度不要求进程应是周期的,也不要求进程的CPU执行的长度是固定的。唯一的要求是:进程在变成可运行时,应向系统宣布截止期限。
比例分享调度
比例分享调度程序在所有应用之间分配了T股。如果一个应用程序接收N股的时间,那么确保了它将有的总的处理器时间。
例如假设总的T=100股要在三个进程A、B、C之间进行分配,A分配到50股,B分配到15股,C分配到20股,这种方案确保:A有50%的总的处理器时间,B有15%,C有20%.
比例分享调度程序采用准入控制策略,以便确保没个进程能够得到分配时间:只有客户请求的股数小于可用的股数时,才能允许客户进入。
POSIX 实时调度
POSIX标准也提供一个实时计算扩展,POSIX定义两种类型的实时线程调度:
- SCHED_FIFO
- SCHED_RR
SCHED_FIFO采用FIFO队列,按照先来先服务策略调度线程,不过在具有同等优先级的线程之间没有分时,所以位于FIFO队列前面的最高优先级的实时线程在得到CPU后会一直占有直到终止或堵塞。
SCHED_RR采用轮询策略,类似SCHED_FIFO,但是提供了在同等优先级的线程之间进行分时。
POSIX还提供一个额外的调度类型SCHED_OTHER,它的实现没有定义,并且取决于特定系统,因此在不同系统上的行为可能不同。
POSIX API提供两个函数,用来获取和设置调度策略:
pthread_attr_getsched_policy(pthread_attr_t *attr, int *policy)
pthread_attr_setsched_policy(pthread_attr_t *attr, int policy)
这两个函数第一个参数是线程属性集的指针。
第二个参数是:
- 获得当前调度策略的整数的一个指针(pthread_attr_getsched_policy)
- 一个整数(SCHED_FIFO、SCHED_RR、SCHED_OTHER,用于pthread_attr_setsched_policy)
如果发生错误,这两个函数会返回非零值。
#include <pthread.h>
#include <stdio.h>
#define NUM_THREADS 5
void *runner(void *param);
int main(int argc, int *argv[]) {
int i, policy;
pthread_t tid[NUM_THREADS];
pthread_attr_t attr;
pthread_attr_init(&attr);
if(pthread_attr_getschedpolicy(&attr, &policy) != 0) {
fprintf(stderr, "Unable to get policy.\n");
}
else {
if(policy == SCHED_OTHER) {
printf("SCHED_OTHER\n");
}
else if(policy == SCHED_RR) {
printf("SCHED_RR\n");
}
else if(policy == SCHED_FIFO) {
printf("SCHED_FIFO\n");
}
}
if(pthread_attr_setschedpolicy(&attr, SCHED_FIFO) != 0) {
fprintf(stderr, "Unable to set policy\n");
}
for(i = 0; i < NUM_THREADS; i++) {
pthread_create(&tid[i], &attr, runner, NULL);
}
for(i = 0; i < NUM_THREADS; i++) {
pthread_join(tid[i], NULL);
}
}
void *runner(void *param) {
pthread_exit(0);
}
这个例子首先确定当前的调度策略,然后将调度算法设置成SCHED_FIFO。