day13 sched调度程序

func schedule

首先看下代码,逻辑其实不复杂,有个地方值得注意,调度前会遍历所有的进程,扫描他的alarm是否设置,并和滴答数比较,如果超过则给进程的alarm喜欢置位。然后遍历所有进程,如果有收到信号并且没屏蔽该信号,且进程的状态是可中断状态,则把进程的状态置位running,也就是说当我们的进程收到了信号之后该进程如果不是明确的不可中断状态,就会进入被调度的队列中。

void schedule (void)
{
    int i, next, c;
    struct task_struct **p; // 任务结构指针的指针。

/* 检测alarm(进程的报警定时值),唤醒任何已得到信号的可中断任务 */

// 从任务数组中最后一个任务开始检测alarm。
    for (p = &LAST_TASK; p > &FIRST_TASK; --p)
        if (*p)
        {
// 如果任务的alarm 时间已经过期(alarmalarm && (*p)->alarm < jiffies)
            {
                (*p)->signal |= (1 << (SIGALRM - 1));
                (*p)->alarm = 0;
            }
// 如果信号位图中除被阻塞的信号外还有其它信号,并且任务处于可中断状态,则置任务为就绪状态。
// 其中'~(_BLOCKABLE & (*p)->blocked)'用于忽略被阻塞的信号,但SIGKILL 和SIGSTOP 不能被阻塞。
            if (((*p)->signal & ~(_BLOCKABLE & (*p)->blocked)) &&
                    (*p)->state == TASK_INTERRUPTIBLE)
                (*p)->state = TASK_RUNNING; //置为就绪(可执行)状态。
        }

  /* 这里是调度程序的主要部分 */

    while (1)
    {
        c = -1;
        next = 0;
        i = NR_TASKS;
        p = &task[NR_TASKS];
// 这段代码也是从任务数组的最后一个任务开始循环处理,并跳过不含任务的数组槽。比较每个就绪
// 状态任务的counter(任务运行时间的递减滴答计数)值,哪一个值大,运行时间还不长,next 就
// 指向哪个的任务号。
        while (--i)
        {
            if (!*--p)
                continue;
            if ((*p)->state == TASK_RUNNING && (*p)->counter > c)
                c = (*p)->counter, next = i;
        }
      // 如果比较得出有counter 值大于0 的结果,则退出124 行开始的循环,执行任务切换(141 行)。
        if (c)
            break;
      // 否则就根据每个任务的优先权值,更新每一个任务的counter 值,然后回到125 行重新比较。
      // counter 值的计算方式为counter = counter /2 + priority。[右边counter=0??]
        for (p = &LAST_TASK; p > &FIRST_TASK; --p)
            if (*p)
                (*p)->counter = ((*p)->counter >> 1) + (*p)->priority;
    }
    switch_to (next);       // 切换到任务号为next 的任务,并运行之。
}

sleep_on函数

明确的睡眠一个进程并且置状态为不可中断的,这里面有个tmp很有意思,tmp是局部变量,是保存在栈上的,然后用二级指针把等待的进程改为自己,而原来位置的等待进程则放到了tmp变量当中。然后把自己状态设置为不可中断,最后执行调度。当执行到调度后的代码的时候,说明该进程是被别的进程明确唤醒了,这时候去检查tmp也就是之前那个被等待的进程,如果存在则把它的状态设置正常。

void sleep_on (struct task_struct **p)
{
    struct task_struct *tmp;

    // 若指针无效,则退出。(指针所指的对象可以是NULL,但指针本身不会为0)。
    if (!p)
        return;
    if (current == &(init_task.task))   // 如果当前任务是任务0,则死机(impossible!)。
        panic ("task[0] trying to sleep");
    tmp = *p;           // 让tmp 指向已经在等待队列上的任务(如果有的话)。
    *p = current;           // 将睡眠队列头的等待指针指向当前任务。
    current->state = TASK_UNINTERRUPTIBLE;  // 将当前任务置为不可中断的等待状态。
    schedule ();            // 重新调度。
// 只有当这个等待任务被唤醒时,调度程序才又返回到这里,则表示进程已被明确地唤醒。
// 既然大家都在等待同样的资源,那么在资源可用时,就有必要唤醒所有等待该资源的进程。该函数
// 嵌套调用,也会嵌套唤醒所有等待该资源的进程。然后系统会根据这些进程的优先条件,重新调度
// 应该由哪个进程首先使用资源。也即让这些进程竞争上岗。
    if (tmp)            // 若还存在等待的任务,则也将其置为就绪状态(唤醒)。
        tmp->state = 0;
}

你可能感兴趣的:(day13 sched调度程序)