Linux0.11 任务调度schedule
函数定义在sched.c当中,如下:
void schedule(void)
{
int i,next,c;
struct task_struct ** p;
/* check alarm, wake up any interruptible tasks that havegot a signal */
for(p= &LAST_TASK ; p> &FIRST_TASK ; --p)
if (*p) {
if ((*p)->alarm &&(*p)->alarm< jiffies) {
(*p)->signal|= (1<
(*p)->alarm =0;
}
if (((*p)->signal& ~(_BLOCKABLE & (*p)->blocked))&&
(*p)->state==TASK_INTERRUPTIBLE)
(*p)->state=TASK_RUNNING;
}
/* this is the scheduler proper: */
while (1) {
c = -1;
next = 0;
i = NR_TASKS;
p = &task[NR_TASKS];
while (--i) {
if (!*--p)
continue;
if ((*p)->state ==TASK_RUNNING && (*p)->counter> c)
c = (*p)->counter, next =i;
}
if (c) break;
for(p = &LAST_TASK; p > &FIRST_TASK; --p)
if (*p)
(*p)->counter= ((*p)->counter>> 1) +
(*p)->priority;
}
switch_to(next);
}
在linux0.11中,最多有64个任务同时存在,任务的相关信息存放一个数组里面,数组的结构(sched.h)如下:
struct task_struct {
/* these are hardcoded - don't touch */
long state; /* -1 unrunnable, 0 runnable, >0 stopped */
long counter;
long priority;
long signal;
struct sigaction sigaction[32];
long blocked; /* bitmap of masked signals */
/* various fields */
int exit_code;
unsigned long start_code,end_code,end_data,brk,start_stack;
long pid,father,pgrp,session,leader;
unsigned short uid,euid,suid;
unsigned short gid,egid,sgid;
long alarm;
long utime,stime,cutime,cstime,start_time;
unsigned short used_math;
/* file system info */
int tty; /* -1if no tty, so it must be signed */
unsigned short umask;
struct m_inode * pwd;
struct m_inode * root;
struct m_inode * executable;
unsigned long close_on_exec;
struct file * filp[NR_OPEN];
/* ldt for this task 0 - zero 1 - cs 2 - ds&ss */
struct desc_struct ldt[3];
/* tss for this task */
struct tss_struct tss;
};
在任务调度中,需要注意4个参数:state和signal,blocked,alarm。State是任务的状态,有5中,在sched.h当中定义:
#define TASK_RUNNING 0//就绪,可运行状态,个人理解为正在运行或者等待运行的任务
#define TASK_INTERRUPTIBLE1 //可中断睡眠状态可有信号,wake_up唤醒,从sys_pause,sys_waitpid,interruptiable_sleep_on进入
#define TASK_UNINTERRUPTIBLE 2 //不可中断睡眠,只能由sleep_on()进入,wake_up函数唤醒
#define TASK_ZOMBIE 3 //僵死状态,不能再被调度
#define TASK_STOPPED 4 //暂停状态,0.11没有实现
Signal是任务接受到的信号图表,每一位代表一个信号,0表示没有信号,1表示有信号,blocked是任务当前屏蔽的信号图表,0表示没有屏蔽,1表示屏蔽信号。
alarm是alarm信号。
任务调度是,从最后一个任务到第一个任务遍历,如果当前任务号对应的任务存在,则处理信号。先处理alarm信号,如果alarm信号存在,则在signal中置位,清楚alarm值,在这里,alarm值应该是从开机到定时器触发的滴答数,而jiffies是从开机到现在的滴答数,这样alarm < jiffies表示的就是已经超过了定时器的触发时间。
如果还有其他未被屏蔽的信号存在,并且任务的状态为可中断睡眠状态,将进程设为就绪状态,等待调度执行。
if (*p) {
if ((*p)->alarm&& (*p)->alarm< jiffies) {
(*p)->signal|= (1<
(*p)->alarm =0;
}
if (((*p)->signal& ~(_BLOCKABLE & (*p)->blocked))&&
(*p)->state==TASK_INTERRUPTIBLE)
(*p)->state=TASK_RUNNING;
}
下面的代码是任务的调度代码,主要是查询一个状态为就绪的,并且时间片最大的,且>0的任务执行,如果所有的任务时间片都为空了,重新分配所有任务的时间片,这里counter = counter /2 + priority,此时就绪状态的任务counter已经为0,但睡眠状态的任务不一定为0,所有重新分配时间片就是按照权值大小重新赋值:
while (1) {
c = -1;
next = 0;
i = NR_TASKS;// NR_TASKS==64;
p =&task[NR_TASKS];
while (--i) { //从最后一个任务,63号任务开始,倒序遍历
if(!*--p)
continue;
if((*p)->state == TASK_RUNNING && (*p)->counter > c)
c= (*p)->counter, next = i;
}
if(c) break;
//在sched.h当中,#defineFIRST_TASK task[0]
// #defineLAST_TASK task[NR_TASKS-1]
for(p= &LAST_TASK ; p > &FIRST_TASK ; --p)
if(*p)
(*p)->counter= ((*p)->counter >> 1) + (*p)->priority;
}
找到相应的任务后,执行switch_to(next);切换到要调度的任务。