UCOS 时间片轮转调度算法详解 (1)

UCOS 一种只支持优先级抢占型调度的操作系统。它不支持同一个优先级有 2 个及以上的任
务同时运作。
我修改的 UCOS 支持不同优先级调度的同时,支持同一个优先级有 2 个以上的任务以时间
片轮转方式进行调度。
这个就是 UCOS 和我修改的 UCOS 的区别。
我修改后的 UCOS,在以后的文段中就称为“FIFO-UCOS”。
FIFO 就是先进先出的意思,和时间片轮转调度差不多的意思。

一. FIFO-UCOS 第一讲--核心思想  

我不对 UCOS 做过多的解释,我本人对 UCOS 的代码也没有看完。
事实上这个代码是我一年半以前做出来的了,当时就是为了帮一个朋友完成论文,并且为我
卖的 S3C44B0 开发板增加一些东西吧。(不过我的板子最后没有卖出去多少,题外话,也许
这个东西并没有多少人热心吧)
这一年半我再也没有从事过软件开发(我一直都是硬件工程师),对 UCOS 也有些遗忘了。
也许有的东西讲错了,还请包函。
当时和那个做论文的朋友讨论的时候,他也设计了一个调度方式,但是事实证明没有我做的
那个好。毕竟它增加了另外一个 PCB 链。

UCOS 写得很好,把很多东西都做得很完善了。我要说的核心思想,就只有下面一小段话,
注意看了哦:

UCOS 在调度的时候,会按照优先级选择进程进行调度,我做的部分就仅仅是在他进行优先
级检查之前,把和运行态同等级的进程进行了替换,替换成了时间片轮转调度的下一个进程!
呵呵,核心思想讲完了。下一节讲解具体代码了。

本人提供 UCOS 在 S3C44B0 上运行的完整代码。
注意,是 UCOS 代码,不是 FIFO-UCOS;

发邮件到我邮箱: keterzhang@hotmail.com
请不要在这个地方回复邮件地址问我要,我不想复制地址那么麻烦,就直接回复你的邮件是
最简单的。
  

二.TCB 的修改  

FIFO-UCOS 和 UCOS 并不能完全兼容,最大的改变,就是对 TCB 的结构做了改动,加入了
我自己的段。
  该部分代码在 UCOS-II.H 文件中:
   typedef struct os_tcb {
   OS_STK *OSTCBStkPtr; /* Pointer to current top of stack */
  
  #if OS_TASK_CREATE_EXT_EN > 0
   void *OSTCBExtPtr; /* Pointer to user definable data for TCB extension */
   OS_STK *OSTCBStkBottom; /* Pointer to bottom of stack */
   INT32U OSTCBStkSize; /* Size of task stack (in number of stack elements) */
   INT16U OSTCBOpt; /* Task options as passed by OSTaskCreateExt() */
    
  #endif
   INT16U OSTCBId; /* Task ID (0..65535) */
  
   struct os_tcb *OSTCBNext; /* Pointer to next TCB in the TCB list */
   struct os_tcb *OSTCBPrev; /* Pointer to previous TCB in the TCB list */
  
  #if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0) || (OS_SEM_EN > 0)
|| (OS_MUTEX_EN > 0)
   OS_EVENT *OSTCBEventPtr; /* Pointer to event control block */
  #endif
  
  #if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0)
   void *OSTCBMsg; /* Message received from OSMboxPost() or OSQPost() */
  #endif
  
  #if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0)
  #if OS_TASK_DEL_EN > 0
   OS_FLAG_NODE *OSTCBFlagNode; /* Pointer to event flag node */
  #endif  
   OS_FLAGS OSTCBFlagsRdy; /* Event flags that made task ready to run */
  #endif
  
   INT16U OSTCBDly; /* Nbr ticks to delay task or, timeout waiting for event */
   INT8U OSTCBStat; /* Task status */
   INT8U OSTCBPrio; /* Task priority (0 == highest, 63 == lowest) */
  
   INT8U OSTCBX; /* Bit position in group corresponding to task priority (0..7) */
   INT8U OSTCBY; /* Index into ready table corresponding to task priority */
   INT8U OSTCBBitX; /* Bit mask to access bit position in ready table */
   INT8U OSTCBBitY; /* Bit mask to access bit position in ready group */
  
  #if OS_TASK_DEL_EN > 0
   BOOLEAN OSTCBDelReq; /* Indicates whether a task needs to delete itself */
  #endif
  //
  //this is my FIFO shedule code
  #ifdef OS_TIME_SCHEDULE
   struct os_tcb *OSTSnext;
   struct os_tcb *OSTSprev;  
   INT8U OSTSLen;
   INT8U OSTSCurLen;
  #endif
  //
  } OS_TCB;
  -------------------------------------
---
  以下代码是我自己加的:
   struct os_tcb *OSTSnext;
   struct os_tcb *OSTSprev;  
   INT8U OSTSLen;
   INT8U OSTSCurLen;
  
  当进程为同一个优先级的时候,*OSTSnext 和*OSTSprev 就形成一个链表,该链表是双
向的,前后形成一个环链表。
   OSTLen 记录了该进程占用多少个时间片;
   OSTSCurLen 记录的是该进程现在还剩下多少个时间片时间可以用;

三.进程创建  

我的语言习惯很差,毕竟做软件不是我的专业。我再次说明,我是硬件工程师。
  把代码给一个朋友看的时候,他直皱眉头。他说:你改过的地方,为什么不用
  #ifdef FIFO_Schedule 来进行选择?
  呵呵,以后的代码可能要让行家郁闷了,很难看,见笑!
  
  进程创建的主要目的是跳过原代码中的优先级占用检查,并在这里加入 FIFO
进程创建。
  文件在 Os_task.c 中:
   INT8U OSTaskCreate (void (*task)(void *pd), void *pdata, OS_STK *ptos,  
INT8U prio,INT16U id, INT8U TSlen)
  {
  #if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
   OS_CPU_SR cpu_sr;
  #endif
   OS_STK *psp;
   INT8U err;
  
  
  #if OS_ARG_CHK_EN > 0
   if (prio > OS_LOWEST_PRIO) { /* Make sure priority is within allowable  
range */
   return (OS_PRIO_INVALID);
   }
  #endif
   OS_ENTER_CRITICAL();
   if (OSTCBPrioTbl[prio] == (OS_TCB *)0) { /* Make sure task doesn't  
already exist at this priority */
    
///

   // if you want run the fifo mask this code
   //OSTCBPrioTbl[prio] = (OS_TCB *)1; /* Reserve the priority to prevent  
others from doing ... */
   /* ... the same thing until task is created. */
  //
   OS_EXIT_CRITICAL();
   psp = (OS_STK *)OSTaskStkInit(task, pdata, ptos, 0); /* Initialize the  
task's stack */
   err = OS_TCBInit(prio, psp, (OS_STK *)0, id, 0, (void *)0, 0, TSlen);
   if (err == OS_NO_ERR) {
   OS_ENTER_CRITICAL();
   OSTaskCtr++; /* Increment the #tasks counter */
   OS_EXIT_CRITICAL();
   if (OSRunning == TRUE) { /* Find highest priority task if multitasking  
has started */
   OS_Sched();
   }
   } else {
   OS_ENTER_CRITICAL();
   OSTCBPrioTbl[prio] = (OS_TCB *)0;/* Make this priority available to  
others */
   OS_EXIT_CRITICAL();
   }
   return (err);
   }
  
    
   else //this is FIFO task create code
   {
   OS_EXIT_CRITICAL();
   psp = (OS_STK *)OSTaskStkInit(task, pdata, ptos, 0); /* Initialize the  
task's stack */
   err = OS_TCBInit(prio, psp, (OS_STK *)0, id, 0, (void *)0, 0, TSlen);
   if (err == OS_NO_ERR) {
   OS_ENTER_CRITICAL();
   OSTaskCtr++; /* Increment the #tasks counter */
   OS_EXIT_CRITICAL();
   if (OSRunning == TRUE) { /* Find highest priority task if multitasking  
has started */
   OS_Sched();
   }
   }
   return(err);
   }
  
   OS_EXIT_CRITICAL();
   return (OS_PRIO_EXIST);
  }
  其中加入的传递参数有两个,一个是 id,一个是 TSLen
  因为会出现同优先级的任务,那么采用优先级做为任务 ID
的方式是不可取的了,我们必须加入任务 id 段
   TSLen 段,是该进程的时间片为多长。
  
  ///符号中的都是我加的代码,注意看注
释很容易看懂的。


四.进程初始化  

进程初始化基本保留以前的东西不动,要的就仅仅是把 TSLen 加入 TCB,把上面说到的那
个链表链接起来。
  
  代码在文件 Os_task.c 中
   INT8U OS_TCBInit (INT8U prio, OS_STK *ptos, OS_STK *pbos, INT16U id, INT32U
stk_size, void *pext, INT16U opt, INT8U TSlen)
  {
  #if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
   OS_CPU_SR cpu_sr;
  #endif  
   OS_TCB *ptcb;
  
   OS_ENTER_CRITICAL();
   ptcb = OSTCBFreeList; /* Get a free TCB from the free TCB list */
   if (ptcb != (OS_TCB *)0) {
   OSTCBFreeList = ptcb->OSTCBNext; /* Update pointer to free TCB list */
   OS_EXIT_CRITICAL();
   ptcb->OSTCBStkPtr = ptos; /* Load Stack pointer in TCB */
   ptcb->OSTCBPrio = (INT8U)prio; /* Load task priority into TCB */
   ptcb->OSTCBStat = OS_STAT_RDY; /* Task is ready to run */
   ptcb->OSTCBDly = 0; /* Task is not delayed */
  
  #if OS_TASK_CREATE_EXT_EN > 0
   ptcb->OSTCBExtPtr = pext; /* Store pointer to TCB extension */
   ptcb->OSTCBStkSize = stk_size; /* Store stack size */
   ptcb->OSTCBStkBottom = pbos; /* Store pointer to bottom of stack */
   ptcb->OSTCBOpt = opt; /* Store task options */
  #else
   pext = pext; /* Prevent compiler warning if not used */
   stk_size = stk_size;
   pbos = pbos;
   opt = opt;
  #endif
   ptcb->OSTCBId = id; /* Store task ID */
  
  #if OS_TASK_DEL_EN > 0
   ptcb->OSTCBDelReq = OS_NO_ERR;
  #endif
  
   ptcb->OSTCBY = prio >> 3; /* Pre-compute X, Y, BitX and BitY */
   ptcb->OSTCBBitY = OSMapTbl[ptcb->OSTCBY];
   ptcb->OSTCBX = prio & 0x07;
   ptcb->OSTCBBitX = OSMapTbl[ptcb->OSTCBX];
  
  #if OS_EVENT_EN > 0
   ptcb->OSTCBEventPtr = (OS_EVENT *)0; /* Task is not pending on an event */
  #endif
  
  #if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0) &&
(OS_TASK_DEL_EN > 0)
   ptcb->OSTCBFlagNode = (OS_FLAG_NODE *)0; /* Task is not pending on an event flag
*/
  #endif
  
  #if (OS_MBOX_EN > 0) || ((OS_Q_EN > 0) && (OS_MAX_QS > 0))
   ptcb->OSTCBMsg = (void *)0; /* No message received */
  #endif
  /
   ptcb->OSTSLen=TSlen;
   ptcb->OSTSCurLen=TSlen;
  /
  #if OS_VERSION >= 204
   OSTCBInitHook(ptcb);
  #endif
  
   OSTaskCreateHook(ptcb); /* Call user defined hook */
    
   OS_ENTER_CRITICAL();
  
   if(OSTCBPrioTbl[prio] == 0)
   {
   OSTCBPrioTbl[prio] = ptcb;
   ptcb->OSTSnext = ptcb;
   ptcb->OSTSprev = ptcb;
   }
   else
   {
   ptcb->OSTSnext = OSTCBPrioTbl[prio]->OSTSnext;
   OSTCBPrioTbl[prio]->OSTSnext = ptcb;
   ptcb->OSTSprev = OSTCBPrioTbl[prio];
   (ptcb->OSTSnext )->OSTSprev=ptcb;
   }
   ///
   ptcb->OSTCBNext = OSTCBList; /* Link into TCB chain */
   ptcb->OSTCBPrev = (OS_TCB *)0;
   if (OSTCBList != (OS_TCB *)0) {
   OSTCBList->OSTCBPrev = ptcb;
   }
   OSTCBList = ptcb;
   OSRdyGrp |= ptcb->OSTCBBitY; /* Make task ready to run */
   OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
   OS_EXIT_CRITICAL();
   return (OS_NO_ERR);
   }
   OS_EXIT_CRITICAL();
   return (OS_NO_MORE_TCB);
  }
  -------------------------------------
--
   if(OSTCBPrioTbl[prio] == 0)
   {
   OSTCBPrioTbl[prio] = ptcb;
   ptcb->OSTSnext = ptcb;
   ptcb->OSTSprev = ptcb;
   }
   else
   {
   ptcb->OSTSnext = OSTCBPrioTbl[prio]->OSTSnext;
   OSTCBPrioTbl[prio]->OSTSnext = ptcb;
   ptcb->OSTSprev = OSTCBPrioTbl[prio];
   (ptcb->OSTSnext )->OSTSprev=ptcb;
   }
  这一段解释一下
  链表必须进行链接,在优先级没有被占用的时候,这个链表的前后都链向自己,这样在
调度的时候,FIFO 调度就算指向下个任务,也指向的是自己,不影响优先级调度。
  当在这个优先级已经有进程的时候,那么我们需要的就是把这个进程加入到这个优先级
的链表中,链表操作相信大家都能看懂,不多做解释。

五.进程调度  

进程调度基本还是依靠 UCOS 自己的调度算法,修改的,就仅仅是在调度之间,把运行态
优先级的任务进行运算,时间片使用完,就指向下一个进程。
  
   Os_core.c
  
  
   void OSTimeTick (void)
  {
  #if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */
   OS_CPU_SR cpu_sr;
  #endif  
   OS_TCB *ptcb;
  
  
   OSTimeTickHook(); /* Call user definable hook */
  #if OS_TIME_GET_SET_EN > 0  
   OS_ENTER_CRITICAL(); /* Update the 32-bit tick counter */
   OSTime++;
   OS_EXIT_CRITICAL();
  #endif  
   if((OSTCBCur->OSTSCurLen)>0)//如果时间片没有用完
   {
   if(--OSTCBCur->OSTSCurLen==0)//时间片在这次调度中用完了
   {
   if(OSTCBCur->OSTSnext != OSTCBCur)//如果优先级里面至少有两个进程(看上一篇
文章来理解这个地方)
   {
   OSTCBCur->OSTSCurLen = OSTCBCur->OSTSLen;//把现在时间片用完的进程的时间
补回来,以便下次调度
   OSTCBPrioTbl[OSTCBCur->OSTCBPrio] = OSTCBCur->OSTSnext;//把该优先级的任
务替换成下一个任务;
   OSPrioCur=0;//把当前的运行优先级改成 0,不然调度认为该优先级的任务没有结束,
不执行任务调度,结果回到的地方,还是上一个时间片的任务,就死翘翘了! }
   }
   }
  
  
   ptcb = OSTCBList; /* Point at first TCB in TCB list */
   while (ptcb->OSTCBPrio != OS_IDLE_PRIO) { /* Go through all TCBs in TCB list */
   OS_ENTER_CRITICAL();
   if (ptcb->OSTCBDly != 0) { /* Delayed or waiting for event with TO */
   if (--ptcb->OSTCBDly == 0) { /* Decrement nbr of ticks to end of delay */
   if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == 0x00) { /* Is task suspended? */
   if(OSTCBPrioTbl[ptcb->OSTCBPrio]==0)//如果该优先级只剩下一个进程了
   {
   OSRdyGrp |= ptcb->OSTCBBitY; /* No, Make task Rdy to Run (timed out)*/
   OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
   OSTCBPrioTbl[ptcb->OSTCBPrio]=ptcb;
   ptcb->OSTSnext=ptcb;//指向自己
   ptcb->OSTSprev=ptcb;//指向自己
   }
   else//增加了链表操作而已,自己看吧,把任务脱出链表而已
   {
   ptcb->OSTSnext=OSTCBPrioTbl[ptcb->OSTCBPrio]->OSTSnext;
   OSTCBPrioTbl[ptcb->OSTCBPrio]->OSTSnext=ptcb;
   ptcb->OSTSprev=OSTCBPrioTbl[ptcb->OSTCBPrio];
   (ptcb->OSTSnext)->OSTSprev=ptcb;
   }
   } else { /* Yes, Leave 1 tick to prevent ... */
   ptcb->OSTCBDly = 1; /* ... loosing the task when the ... */
   } /* ... suspension is removed. */
   }
   }
   ptcb = ptcb->OSTCBNext; /* Point at next TCB in TCB list */
   OS_EXIT_CRITICAL();
   }
  }
  
  
  该函数是 UCOS 调度的关键函数,也是 FIFO 最核心的东西。
  我知道有些东西多余了,比如完全没有必要判断是否这个优先级只有一个任务。但是,
这个是任务调度处理中,消耗的 OS 时间,也就是调度效率,我个人认为,调度占用时间越
短越好。呵呵。
  
   UCOS 调度的文章就写到这里,信号量等其他地方修改还请大家自己动手了!



10 楼: 张 SIR 真是好样的。我也是做硬件的,有一段时间没玩过 UCOS 了  

最近也写了一个调度程序(不敢说是操作系统),实现多个任务的伪并行运行。
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值