linux0.11学习之schedule

在一个操作系统中,最重要的部分就是任务调度,linux的0.11版本的任务调度函数比较简单,所以学习任务调度的原理非常适用。

下面我们贴出来schedule函数的代码。

  1
  2 /*
  3  *  'schedule()' is the scheduler function. This is GOOD CODE! There
  4  * probably won't be any reason to change this, as it should work well
  5  * in all circumstances (ie gives IO-bound processes good response etc).
  6  * The one thing you might take a look at is the signal-handler code here.
  7  *
  8  *   NOTE!!  Task 0 is the 'idle' task, which gets called when no other
  9  * tasks can run. It can not be killed, and it cannot sleep. The 'state'
 10  * information in task[0] is never used.
 11  */
 12 void schedule(void)
 13 {
 14     int i,next,c;
 15     struct task_struct ** p;
 16
 17 /* check alarm, wake up any interruptible tasks that have got a signal */
 18
 19     for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
 20         if (*p) {
 21             if ((*p)->alarm && (*p)->alarm < jiffies) {
 22                     (*p)->signal |= (1<<(SIGALRM-1));
 23                     (*p)->alarm = 0;
 24                 }
 25             if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) &&
 26             (*p)->state==TASK_INTERRUPTIBLE)
 27                 (*p)->state=TASK_RUNNING;
 28         }
 29
 30 /* this is the scheduler proper: */
 31
 32     while (1) {
 33         c = -1;
 34         next = 0;
 35         i = NR_TASKS;
 36         p = &task[NR_TASKS];
 37         while (--i) {
 38             if (!*--p)
 39                 continue;
 40             if ((*p)->state == TASK_RUNNING && (*p)->counter > c)
 41                 c = (*p)->counter, next = i;
 42         }
 43         if (c) break;
 44         for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
 45             if (*p)
 46                 (*p)->counter = ((*p)->counter >> 1) +
 47                         (*p)->priority;
 48     }
 49     switch_to(next);
 50 }

从代码注释中,我们可以看到linus本人对这段代码是比较推崇的。这段代码可以运行在任何边界,没有理由修改这段代码。有一点需要注意的是信号处理的代码。任务是一个空闲任务,只有在其他任务没有运行时该空闲任务才会被运行。空闲任务不能 被杀死,也不能睡眠。它的tcb即task[0].state,任务状态信息从来不用的。

接下来我们来分析代码。

14,15行定义了一些变量,要说明的是p 这是一个双重指针,双重针指向的类型是任务数据块.数据块里面存放着一个任务的所有信息。

19-28行的代码作用是进行的报警定时值,唤醒任何已经得到信号的可中断任务。



19行开始for循环从最后一个任务开始向前开始遍历所有的任务。

20行判断该任务是否存在,不存在则跳过。

21行判断如果设置过该任务的定时值,且该定时值已经过期则,在信号位图中置位SIGALRM,并清alarm.

要说明的一点是jiffies变量是系统开机开始算起的计时数  10ms/滴答

25.26行如果信号位图中除了被阻塞的信号外还有其它信号,并且任务处于可中断状态,则任务置位就绪态。 ~(_BLOCKABLE & (*p)->blocked)为忽略阻塞信号。

32到48行是调度程序中主要的部分。

先对几个变量进行说明。

(*p)->counter 表示 任务状态的剩余滴答计数。

i表示任务标号,p表示任务数组的地址。

c和next用来保存(*p)->counter和i

这里需要对i和p进行说明。初始

i = NR_TASKS;

p =&task[NR_TASKS]

这两个都是无效值,因为NR_TASKS,用宏定义为64,也就是最多运行64个任务。但因为有任务0,所以任务数只能从0-63.


所以在37,38行判断前对i和p都进行了前减减操作。

39行也是为了判断任务存在,不存在的任务跳过。

40和41行是为了找到当前所有任务中即处于就绪态,且剩余的滴答计时最多的任务。

43行如果找到当前所有任务中即处于就绪态,且剩余的滴答计时最多的任务。跳出循环到49行进行任务切换。

如果当前就绪的所有任务的剩余的滴答数都为0,则重新为所有任务都分配滴答数。

45行为跳过不存在的任务。

46.47行为任务重新分配滴答数。

(*p)->counter = ((*p)->counter >> 1) +  (*p)->priority;

算法为counter = counter/2 + priority

44到48行是为所有的任务都重新分配滴答数。因为之前40行判断的时候可能处于非就绪状态的任务滴答数可能不为0,所以本次分配要对那些在40行判断非就绪状态的任务的滴答数加上之前剩余的一半 再加上自己的优先级。

此时系统中就绪态的任务的滴答数已经重新分配,所以循环回32行,可以重新找到就绪态的最大滴答数的任务。

然后在43行通过break退出循环,执行任务切换。


你可能感兴趣的:(linux)