sched.c 是内核关于调度的程序,主要有三个分别是,schedule(),sleep_on(),wake_up
schedule()核心内容如下:
while (1) {
c = -1;//存储任务的counter
next = 0;
i = NR_TASKS;
p = &task[NR_TASKS];
while (--i) {
if (!*--p)//如果*p为空的话,表示当前这个任务号没有任务,继续下一个任务号
continue;
if ((*p)->state == TASK_RUNNING && (*p)->counter > c)
c = (*p)->counter, next = i;
}//选择具有最大counter值的任务,并且任务号为next
if (c) break;//如果选完一轮,所有的任务counter都已经用完,或者没有任务处于就绪态,
//那么这时跳出while循环,next为0,切换到任务0执行
//如果c==-1,则重新计算任务时间片,否则跳出while循环
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
if (*p)
(*p)->counter = ((*p)->counter >> 1) +
(*p)->priority;
/*
//如果所有任务的时间片都已经用完,则根据任务优先级重新计算时间片
这样睡眠的任务就会获得较大的时间片,当它醒过来时就会被优先执行。
*/
}
switch_to(next);//如果需要切换到当前任务,就不切换退出,否则执行切换。
}
Linux-0.11内核的调度算法的核心是基于优先级的排队算法。
Sleep_on(),当一个进程所请求的资源正忙或者不在内存中,暂时将一个任务切出去放在等待队列中一段时间。当任务切换回来后,继续执行。放入等待的方式是利用函数中的tmp指针作为各个正在等待任务的联系。
void sleep_on(struct task_struct **p)
{
struct task_struct *tmp;
if (!p)
return;
if (current == &(init_task.task))
panic("task[0] trying to sleep");
tmp = *p;
*p = current;
current->state = TASK_UNINTERRUPTIBLE;
schedule();
if (tmp)
tmp->state=0;
}
/*
*p是队列头指针,在执行schedule之前,我们把原队列的头指针放到tmp中,让*p指向
当前任务数据结构,由于p是指针的指针,因此相当于改变了队列的头指针指向,将当前
任务结构放到队列中,如果在切换到新任务后,新任务还是需要使用此资源,那么它同样
会调用sleep_on(),同时将自己加入到任务队列中。加入到队列中的任务都是在等待同一个资源。
当内核某处代码调用以该队列头指针为参数的wake_up(*p)函数时,最后一个陷入的进程会接着
schedule后面继续执行,将tmp的状态编程就绪态,以此开始像解锁链条似得,解锁这个队列。
*/
唤醒操作函数wake_up()把正在等待可用资源的指定任务设置为就绪态
内核中关于定时器的部分
static struct timer_list {
long jiffies;
void (*fn)();
struct timer_list * next;
} timer_list[TIME_REQUESTS], * next_timer = NULL;
添加定时器
1.在timer_list中寻找空闲项
2.将新建的定时器加入到队列中
3.对队列进行调整,按照jiffies从小到大排列,在排序时减去前排的滴答数,这样在处理定时器时只要查看链表头的第一项的定时是否到期即可。
for (p = timer_list ; p < timer_list + TIME_REQUESTS ; p++)
if (!p->fn)
break;//寻找空闲的定时器项
if (p >= timer_list + TIME_REQUESTS)
panic("No more time requests free");
p->fn = fn;
p->jiffies = jiffies;
p->next = next_timer;
next_timer = p;//将新建的定时器加入到队列当中,并设置头指针,并且可以看出链表的顺序是和数组索引相反的
while (p->next && p->next->jiffies < p->jiffies) {
p->jiffies -= p->next->jiffies;
fn = p->fn;
p->fn = p->next->fn;
p->next->fn = fn;
jiffies = p->jiffies;
p->jiffies = p->next->jiffies;
p->next->jiffies = jiffies;
p = p->next;
}/*链表项按值从小到大排序,在排序时减去前排需要的滴答数,这样处理后
只需要查看链表第一项定时到否到期即可。但是这里程序是有问题的,当最新
加入的定时器时间小于原来头部定时器的时间时,是不会进入中断的,但是实际
上,还是需要将原头部定时器的值减去新加入的头部的值。
do_timer:对当前到时的定时器,进行处理
if (next_timer) {
next_timer->jiffies--;//timer_list中第一项时间-10ms
while (next_timer && next_timer->jiffies <= 0) {
void (*fn)(void);//函数指针声明
fn = next_timer->fn;//取得中断处理程序
next_timer->fn = NULL;
next_timer = next_timer->next;//移动定时器队列
(fn)();//执行处理函数
}
}
fork.c:
主要包括四个函数find_empty_process()和copy_process,进程内存区域验证函数,copy_mem()页表拷贝函数
copy_process:创建并复制进程的代码段和数据段以及环境。系统首先为新建进程在内存区中申请一页内存来存放任务数据结构信息,并复制当前进程任务数据结构中的所有内容作为新进程任务数据结构。
copy_mem:为新任务在线性地址空间设置代码和数据段基址、限长,复制内存页表。
find_empty_process():在返回任务数组中任务号。
阅读(1551) | 评论(0) | 转发(0) |