第三章 APO用户系统类的实现

来源:互联网 发布:js的split 编辑:程序博客网 时间:2024/04/29 14:57

                                第三章   APO用户系统类的实现

 
每当写新一章,前面的章节都要重改;这没办法。

      APO系统中一个进程可以在内核态(kernelmode)或用户态(user mode)下执行,并且分别使用各自独立的内核态堆栈MSP和用户态堆栈PSP及各自的PC、寄存器组等;就像有2个CPU一样,我们可以看作是有2个核;这种32位核,占IC空间很小;即使集成个千核也没问题。 APO的CPU核,本质就是小体积的32位核;但外部数据通道是256位的,集成的多功能硬件模块是64K位的;还有强大运算能力的GPU。CPU寄存器可16位、32位、一行256位(传送)的方式。 在内核态运行的是内核进程;在用户态下运行的是用户进程。有2个CPU线路:内核CPU线路,用户CPU线路。内核代码和实时用户进程运行在内核CPU线路。实时用户进程、内核中断服务后半部是在内核调度和运行的内核线程;而内核中断服务前半部是硬件调度的硬内核线程;关于内核态另文讨论。

     软实时、普通用户进程则是运行在用户CPU线路的采用动态优先级调度的用户进程。用户堆栈用于进程在用户态下临时保存调用函数的参数、局部变量等数据;内核堆栈则含有内核程序执行函数调用时的信息;每个用户进程都有一个用户堆栈。通常中断只是打断内核线程;用户进程除了一个消息中断(用于系统消息通知),和“时钟滴答”(tick= 10ms)中断及异常中断外;在其它硬件中断时还是继续运行在用户CPU线路上。

一、用户进程(CPU线路)


1、APO将任务主要划分为4个状态:

(1)运行状态(TASK_RUNNING)。
       当进程正在被CPU执行,则称该进程处于运行状态(RUNstate)。若此时进程没有被CPU执行,又已经完成任务运行前的所有准备工作,但由于具有更高优先权的任务在运行而未执行,在这种状态下的任务一旦成为优先权最高的任务,其将被执行;则称其处于就绪运行状态(READYstate)。当系统资源已经可用时,进程就被唤醒而进入准备运行状态,也会进入就绪态(READYstate)。这些状态在内核中表示方法相同,都被称为处于TASK_RUNNING 可运行状态。

BU8K RUNWT{ // 可运行状态标志位图,共32行。进程进入运行或就绪;相应位置1

  BU256  rtask_RUNNING;   // 软实时进程的运行状态标志位图

  BU256  task_RUNNING;    // 普通进程的的运行状态标志位图,有31行

  …                      // 全局变量,属于CPU类属性区。

}

/*  当start()方法调用时,进程首先进入可运行状态。从阻塞挂起、睡眠状态回来后重新设置RUNWT运行状态标志位图的相应位,也返回到可运行状态。

*/

(2)挂起态(Pend):

      进程需等待某些不可马上利用的资源而被阻塞的状态。这时,系统不会调度该进程执行。只有,当系统释放了进程正在等待的资源,或者进程收到一个相关消息,才可以唤醒进程转换到就绪状态。管理者之间的消息、系统消息、资源消息等等,都是内核消息管理员管理的;给进程发消息、唤醒进程等等都是它的事情。进程挂起时,置进程挂起标志PRR.pend。它先保存其当前进程的H0-H3行寄存器到内存对应i节点数据区,之后把对应动态优先级值清0,RUNWT运行状态标志位图的相应位被清0,进程不再参与调度;使硬件优先级判断部件动作,置需调度标志,安排下一个更高优先级的进程;调用进程切换方法switch。当系统唤醒进程转换到就绪状态;则从内存对应i节点数据区COPYJI_inode.counter到对应的动态优先级值位置;并使硬件优先级判断部件动作。

(3)休眠态(Sleep):

        如果系统不需要某一个进程工作,则这个进程被屏蔽而处于休眠状态;可以休眠一段时间后再启动。位图RUNWT相应位置清0,对应动态优先级值清0。

(4)静止态(DORMANTstate):
      进程建立,但未开始运行;还没在其上调用start()方法。

       8K/256= 32E(行)的进程号分配位图FPWT描述进程号分派标志。实时进程的进程号在内核管理的位图;软实时进程的进程号是0—255,对应第0行位图; 普通进程的进程号是256—--8K-1,对应第2行到第31行的位图。打开一个进程,系统会在FPWT位图寻找一个空闲位并置1,从而为该进程分配进程号。

BU8K  JIWT{         // 进程号分配位图,共有32行;

  BU256  rtask_pid; // 第0行对应的是256个软实时进程的进程号。

  BU256  task_pid; // 第1行起对应的是8K-256个普通进程的进程号;共31行。

…                  // 全局变量,属于CPU类属性区。

}


2、进程调度

 
        APO采用了基于动态优先级的抢占式调度算法。当一进程的优先级变动并进入就绪态时,如果其优先级比当前运行的进程优先级高;内核就会置需调度标志。当前运行进程的动态优先级,每次“时钟滴答”(tick= 10ms)都会减一。这时,都会做优先级判断;而这一优先级判断过程是硬件实现的,需10个指令周期(10ns)。
 
         当一个进程的运行时间片用完,系统就会使用调度程序强制切换到其他的进程去执行。另外,如果进程需要等待系统的某个资源,此时该进程就会调用CPU.sleep()或自愿地放弃CPU的使用权,而让调度程序去执行其他进程;进程则进入挂起态(Pend)。通常,“时钟滴答”(tick= 10ms)中断时;如果需调度标识置1,可抢占标志为1;中断程序才会进行进程切换操作。在“可抢占标志”为0时,进程不能被其他进程抢占。每个进程都有自己的内存空间,一个进程不能改变另一个进程的状态。

        对于实时进程,APO 采用了两种调度策略,即先来先服务调度(First-In, First-Out , FIFO )和时间片轮转调度(Round Robin , RR )。在APO中,FIFO实时进程在内核CPU线路中调度和运行。

      SCHED_FIFO:不同的进程根据静态优先级进行排队,谁先准备好运行就先调度谁,并且正在运行的进程不会被终止直到以下情况发生:
(1)被有更高优先级的进程所强占CPU;
(2)自己因为资源请求而阻塞;(调用CPU.sleep())。
(3)自己主动放弃CPU(调用CPU.yield())。

     当 policy 的值为SCHED_FIFO 时,遵守 POSIX1.b 标准的FIFO 调度规则。它会一直运行,直到有一个进程因 I/O 阻塞,或者主动释放CPU ,或者是 CPU 被另一个具有更高rt_priority 的实时进程抢先。在 APO 实现中,SCHED_FIFO 进程仍然拥有时间片,只有当时间片用完时它们才被迫释放CPU 。因此,如同 POSIX1.b 一样,这样的进程就象没有时间片 (不是采用分时 ) 一样运行。 APO中进程仍然保持对其时间片的记录(不修改 counter )主要是为了实现的方便。通常会把运行时间较短的进程设置为FIFO调度策略,把运行时间较长的进程设置为RR调度策略。

SCHED_RR:

       这种调度策略跟上面的SCHED_FIFO一样,除了它给每个进程分配一个时间片,时间片到了正在执行的进程就放弃执行;时间片的长度可以通过CPU.get(JI_inode.rr)调用得到。

      SCHED_OTHER调度策略中,调度器总是选择那个counter值最大的进程来调度执行。从逻辑上分析SCHED_OTHER调度策略存在着调度周期(epoch),在每一个调度周期中,一个进程的counter值的大小影响了当前时刻应该调度哪一个进程来执行,其中priority是一个固定不变的值,在进程创建时就已经确定,它代表了该进程的优先级,也代表这该进程在每一个调度周期中能够得到的时间片的多少;counter是一个动态变化的值,它反映了一个进程在当前的调度周期中还剩下的时间片。在每一个调度周期的开始,priority的值被赋给counter,然后每次该进程被调度执行时,counter值都减少。当counter值为零时,该进程用完自己在本调度周期中的时间片,不再参与本调度周期的进程调度。当所有进程的时间片都用完时,一个调度周期结束,然后周而复始。另外可以看出 APO系统中的调度周期不是静态的,它是一个动态变化的量,比如处于可运行状态的进程的多少和它们priority值都可以影响一个epoch的长短。可见SCHED_OTHER调度策略本质上是一种比例共享的调度策略,它的这种设计方法能够保证进程调度时的公平性--一个低优先级的进程在每一个epoch中也会得到自己应得的那些CPU执行时间,另外它也提供了不同进程的优先级区分,具有高priority值的进程能够获得更多的执行时间。

      当policy 的值为 SCHED_OTHER 时,是普通的用户进程,采用动态优先调度,选择进程的依据就是进程counter 的大小。进程创建时,优先级 priority 被赋一个初值,一般为 0~ 8K 之间的数字,这个数字同时也是计数器 counter的初值,就是说进程创建时两者是相等的。字面上看, priority 是“优先级”、counter 是“计数器”的意思,然而实际上,它们表达的是同一个意思,即进程的“时间片”。priority代表分配给该进程的时间片, counter 表示该进程剩余的时间片。在进程运行过程中,counter 不断减少,而 priority 保持不变,以便在counter 变为 0 的时候(该进程用完了所分配的时间片)对counter 重新赋值。当一个普通进程的时间片用完以后,并不马上用priority 对 counter 进行赋值,只有所有处于可运行状态的普通进程的时间片都用完了以后,才用priority 对 counter 重新赋值,这个普通进程才有了再次被调度的机会。这表明,普通进程在运行过程中,counter 的减小给了其它进程得以运行的机会,直至 counter 减为 0时才完全放弃对 CPU 的使用,这就相当于优先级在动态变化,所以称之为动态优先级调度。时间片的概念和其他操作系统是一样的,APO 的时间单位也是“时钟滴答”,只是不同操作系统对一个时钟滴答的定义不同而已,APO 为 10ms 。进程的时间片就是指多少个时钟滴答,内核创建新进程时分配给进程的时间片缺省为1K tick = 1024ms ~= 1s ,用户可以通过CPU类的方法调用改变它。

       当policy 的值为 SCHED_RR 时,表示软实时进程。最多有256个软实时进程,每个实时进程都有一个rt_priority,其作用与SCHED_OTHER进程的priority作用一样。软实时进程的rt_priority大于等于32K。当SCHED_RR 进程的时间片用到剩下8K后,停止对其调度,也不马上用rt_priority对counter进行赋值,只有所有处于可运行状态的软实时进程的时间片都用到剩下8K了以后,才用rt_priority对counter重新赋值,这个软实时进程才有了再次被调度的机会。只要系统中有一个软实时进程在运行,则任何SCHED_OTHER 进程都不能在CPU 运行。

         每个进程都有优先级priority数值。还没有创建,即还沒分配进程号的进程其priority是0。所有进程的优先级值构成有512行的数组BU16 [8K] priority; 在CPU类属性区;数组的下标是进程号。当开始一个新的软实时进程调度周期时:
COPY.E( counter,priority, #16 );  // 19ns。
当开始一个新的普通进程调度周期时,
COPY.E( counter.256.E, priority.256.E, 496 ); 248ns。
当“时钟滴答”(tick= 10ms)中断到来时;用户进程被打断,保护现场(保存H0-H3)。进入中断程序后,当前进程的counter先减一;之后,专用硬件模块约在10ns判断出其最大值,并与当前进程的counter(B1H)比较,如大于则连同进程号存到B1寄存器;同时置状态寄存器的需调度标志。对数组BU16  counter[8K]中的任一行中的任一字符写都需要进行硬件判断最大值及相关操作。counter变化除了每次tick的减一外,进程收到的消息也会对其加一个数;数的大小取决于消息的紧迫性。一个低优先级的进程或许由于鼠标点击消息而获得焦点而成为较高优先级的进程。

动态优先级寄存器区:
BU512E DYV_priority{  // 动态优先级分为32组,每组有一个组内counter最大值。
BU16 [8K] counter; // 所有的动态优先级占512行,CPU的字符寻址区:H256---H767。
//  H768---H1023是多功能硬件模块区;H191—H255为进程i节点寄存器区域。
//  CPU的片内RAM: H32—H1023共1KE = 8KW = 256K位,全部可字符、字、行寻址。
//  H0-H255的64K位是可寻址的。H0—H31是寄存器区,其中H0-H3是用户寄存器区。
//  分为64个Z寄存器R0H、R0L、….R31H、R31L或32个W寄存器R0-R31或1K位。

}

          8K/256= 32E(行)的TWT位图描述counter减为 0的时间片用完标志。第0行是256个软实时进程的时间片用完标志位图。其它行是普通用户进程的时间片用完标志位图。位图的每一位对应相应进程号的进程,位为1表示相应进程的时间片未用完。8K位图的初始状态全0,进程开始新的周期时,置相应位为1,counter= priority 。每次tick,当前进程的counter-1。当普通进程counter减为 0时,清位图的相应位为0;如果是软实时进程,并判断第0行为0?如是,置就绪状态的所有软实时进程相应时间片用完标志位图位为1,并counter= ty_priority;开始新一轮软实时进程运行周期。如果是普通进程,并判断第1行开始的所有位图行为0?如是,置就绪状态的所有普通进程相应时间片用完标志位图位为1,并counter= priority;开始新一轮普通进程运行周期。TWT位图源自运行位图RUNWT,每一个已建立需运行的沒被屏蔽的进程其进程号对应的位都为1。

BU8K TWT{           //  进程的时间片用完标志位图, 共32E。

  BU256  rtask_counter8k;  // 软实时进程的时间片用完标志位图

  BU256  task_counter0;    // 普通进程的时间片用完标志位图,有31行

  …                      // 全局变量,属于CPU类属性区。

}

/*  如果所有进程的时间片用完,就需要重置BU16 counter [8K]数组;COPY指令会耗费256ns的时间,相当于要多花256个指令周期;这会增大时钟中断的时间损耗。所以,系统安排一个最后的守护进程ZVHWUWHU;它的进程号是8K-1,优先级就一个时间片。当除它外的所有进程时间片用完时,才会轮到它运行;它的任务就是:
COPY priority[8K]到counter[8K];重置RUNWT位图、TWT位图。
*/

二、进程调度类


待续。。。



0 0