今天看了一下0.11核的进程调度部分,发现在这个版本里的进程调度和现在的版本比很简单。下面我就简单说一下0.11核中进程调度的工作原理。
0.11核中进程的调度主要由四个部分数组成:调度初始化、调度、睡眠、唤醒。
一、调度初始化:sched_init()
我们知道在内核初始化(main)过程中,要将任务0转换到用户态下执行,也就是建立Linux中的第一个用户程序。既然任务0之前是在内核态下执行的,所以用的是系统的ldt、和tss,要在用户态下执行,就要使用用户态下的ldt、tss,那么,调度初始化的其中一个任务就是建立任务0的ldt和gdt。下面说一下sched_init()的具体工作过程:
1)在gdt中设置ldt、gdt:ldt和gdt的定义在sched.h中。
2)将eflags中的NT置位:记得在前面关于任务切换的说明中,我提到在main的中sched_init()之后,要调用move_to_user_mode()实现核心态到用户态的转换,而在move_to_user_mode()中,最后调用了iret,如果将NT为0,就不会引起进程的切换了。
3)加载tss和ldt:手动加载仅这一次,以后都是通过任务的切换CPU自动加载的。在这里,因为是第一个用户任务,所以需要手动设置。
4)初始化8253定时器。
5)在IDT中设置时钟的中断门和系统调用的中断门。
二、调度:schedule()
这个函数的主要工作就是从所有处在就绪状态的进程中选择下一个要运行的进程。具体工作过程是这样的:它首先检查在处在可中断睡眠状态下的进程是否除了阻塞信号以外别的信号,如果存在,则将它置成就绪状态。然后循环检查任务数组中的所有处在就绪状态的任务,挑选出剩余执行时间值最大的一个任务,利用switch_to()函数切换到该任务。如果发现所有就绪进程的时间片都是0,则根据任务的优先级,重新计算每个任务的时间片,在重新执行循环检查所有就绪任务的执行剩余时间。
其中,这个函数的核心之一就是switch_to()函数,它能实现任务的切换,它的定义在sched.h中,主要原理就是利用如果ljmp的操作数是任务门描述符就会引起任务的切换。
三、睡眠sleep_on()、interruptible_sleep_on()
在接收这个函数的工作原理之前,先了解一个数据结构:等待队列。等待队列其
其实就是一个进程指针链表,所有由于等待同一个资源的睡眠进程组成一个等待队列。这个链表的连接并不是通常意义上的通过next连接,而是通过tmp,每个睡眠进程都有一个tmp指针,它指向这个进程之前睡眠的那个进程,这样就将等待同一个资源的睡眠进程连接起来了。每个进程睡眠的时候,就将自己插入到这个链表的第一个,这样后睡眠的进程排在前面;被唤醒的时候,还要把它后面的进程,也就是在它之前睡眠的进程也要唤醒,目的是在自己使用完这资源之后,可以让之前的进程使用该资源,这个进程再唤醒它之前的进程……,这样逐个唤醒这个等待队列中的所有进程。简言之,当自己需要的资源不在的时候,就一个个进入等待队列,当资源回来的时候,再一个个唤醒使用这个资源。
睡眠函数的主要功能就是当一个进程所请求的资源正忙或者不再内存是,就将该进程放到等待队列等待一段时间。当进程切换回来后又继续接着运行。这里有两个函数:sleep_on()核interruptible_sleep_on(),分别是将进程以不可中断的形式和可中断的形式放入等待队列。先说一下sleep_on():
这个函数主要有三个部分,第一部分是将自己置为不可中断的睡眠状态,并插入到等待队列的第一个,等待队列是一个task_struct结构的指针链表,但这个链表的连接不是通过next指针,而是通过tmp。这里做到就是链表的插入。
其中*p指向链表头,也就是指向最后加入到等待队列的进程指针(也就是当前进程指针),tmp是原等待队列中第一个进程的指针,也就是在当前进程之前的那个睡眠进程的指针。第二部分就是调用schedule()进行进程的调度,也就是选择要执行的进程。第三部分是当这个进程又重新执行(唤醒)时需要干的事情,当这个进程被唤醒的时候,会继续执行schedule()后面的代码,这段代码的主要工作就是将tmp指向的进程,也就是当前进程在等待队列的时候在它之前进入等待对队列的进程唤醒。
Interruptible_sleep_on()的功能和sleep_on()差不多,都是插入等待队列、调度、恢复后的处理。不同之处一个是当前进程被置成了可中断的睡眠状态。再一个就是恢复后的工作上,重新执行后,唤醒它之前睡眠的进程之前,还要唤醒它之后进入的进程:先检查自己是不是等待队列的头,也就是检查自己是不是当前最后睡眠的进程,如果不是头,就说明在自己进入等待队列之前还有进程进入了等待队列,这个时候要让自己暂且继续等待,先唤醒之前的进程。当然了,不用担心自己不会醒来,因为上面说了,被唤醒的进程还有唤醒在它之前睡眠的进程,这样自己终会被唤醒的。
四、唤醒 wake_up()
就一个工作,将等待队列的头进程的状态置成就绪态。
阅读(817) | 评论(0) | 转发(0) |