搜索
bottom↓
回复: 43

OS空闲为什么一定要运行空闲任务?个人认为空闲运行主函数更好。

[复制链接]

出0入0汤圆

发表于 2009-6-15 18:46:36 | 显示全部楼层 |阅读模式
OS空闲为什么一定要运行空闲任务?个人认为空闲运行主函数更好。

单独的“空闲任务”又没多大用处,还浪费堆栈空间,且使用“空闲任务”后,就不能使用主函数的堆栈了,二次浪费。

任务空闲直接切换到主函数是不是更好,省掉了“空闲任务”,且能充分利用主函数堆栈空间。
另外,还有一个好处是,能更好的与前后台系统耦合,甚至可认为本身就是一种前后台系统。

通常的前后台系统是:
while(1)+中断

现在变成:  
while(1)+任务+中断

如果把任务看做是一种特殊的中断,主函数切换到任务当作中断开始,任务切换到主函数当作是中断返回,则又回到了前后台系统。

当然,也可以把空闲任务当主函数用,但总是没有把主函数当做空闲任务来得好。

出0入0汤圆

发表于 2009-6-15 18:50:49 | 显示全部楼层
空任务一般运行CPU休眠指令,这样可以省电。

出0入0汤圆

发表于 2009-6-15 20:02:22 | 显示全部楼层
关键不在空闲任务上,而是当所有用户任务都被阻塞的时候,这个时候系统应该运行到哪里去。

如果对系统内存使用得抠门一些的话,可以把空闲任务使用的栈压到最小去

出0入0汤圆

 楼主| 发表于 2009-6-15 20:45:06 | 显示全部楼层
空任务一般运行CPU休眠指令,这样可以省电。
---------------------------------------------
主函数一样可以用休眠指令,并不矛盾。


关键不在空闲任务上,而是当所有用户任务都被阻塞的时候,这个时候系统应该运行到哪里去。
-------------------------------------------------------------------------------------
这时候切换回主函数。


如果对系统内存使用得抠门一些的话,可以把空闲任务使用的栈压到最小去
-------------------------------------------------------------------
还怎么抠门,总得给足上下文切换堆栈。另外,主函数的堆栈呢?完全没有用到,这也是浪费。

出0入0汤圆

 楼主| 发表于 2009-6-15 20:47:43 | 显示全部楼层
有时间尝试一下,把ucos空闲任务剔除掉,由主函数代替。

出0入0汤圆

发表于 2009-6-15 20:53:23 | 显示全部楼层
用于统计CPU使用率?

出0入0汤圆

发表于 2009-6-15 21:04:19 | 显示全部楼层
主函数还能切得回去吗?

如果少了空闲任务,调度器会复杂一些。主任务的栈在调度器启动起来后是废掉了的,这个时候可以把它回收用于其他。

如果说主函数还能切换回去,那么是否意味着这是一个空闲任务?

出0入0汤圆

发表于 2009-6-15 21:16:43 | 显示全部楼层
空闲任务作用挺大的,使用休眠指令、进行调度统计等
不过用在嵌入式上应该是省电目的居多
其实“函数”和“任务”都是概念性的东西
在多任务操作系统进行调度后,每一个函数其实都是在一个任务中执行
有一个例外就是主函数,因为进入主函数时多任务环境还未建立,需要进行必要的初始化后启用操作系统的多任务,而主函数运行时的现场如何处理不同OS有不同的选择,一般情况下是永不切换回来,造成楼主所说的“堆栈浪费”
将主函数现场保留为其中一个任务,可以被重新调度执行,然后插入休眠指令,固然可以达到楼主所说的效果,但此时主函数已经充当的空闲任务的角色
一般情况下并不这么做,是为了保证操作系统的良好结构,确保通用性、完备性和可移植性
操作系统内核和用户任务是需要剥离的
而休眠指令往往是处理器相关,甚至是特权级的指令,将它暴露在用户进程,特别是主函数这样和语言及编译器相关的特殊位置,有违操作系统的本意,因此将它作为空闲任务封装在操作系统内部,牺牲一些堆栈空间通常时值得的
当然在深嵌入应用中,操作系统和用户任务本身编译为一体的二进制代码时,这种做法未尝不是个选择,说明楼主非常善于观察和思考,pfpf~

出0入0汤圆

发表于 2009-6-15 21:29:46 | 显示全部楼层
不是有人说可以用来 放看门狗

出0入0汤圆

发表于 2009-6-15 22:51:12 | 显示全部楼层
楼主你太牛了,这样的想法真是想他人不敢想,思他人不敢思。
如果没有就绪任务时,你能把CPU停下来,然后等任务就绪后,你再让CPU运转,
那你就成功了。
否则CPU就没去处了

出0入0汤圆

发表于 2009-6-15 23:07:03 | 显示全部楼层
CPU总要有东西在运行的,空闲任务不就是在那浪费时间的吗,没有任务的话,USOS会崩溃掉的

出0入0汤圆

发表于 2009-6-15 23:13:06 | 显示全部楼层
没有任务,人会崩溃

出0入0汤圆

 楼主| 发表于 2009-6-15 23:44:21 | 显示全部楼层
楼上几位并没有理解我的意思。

CPU总要有东西在运行的,任务空闲时,运行空闲任务。
但是,为什么一定要运行空闲任务?就不能运行其他函数?

我的意思就是让任务空闲时去运行主函数,当有任务就绪,再从主函数切换到任务。

主函数可以原先的空闲任务控制块参与调度。
也可以把任务控制块省去,只使用一个断点上下文指针,
当所有任务挂起,直接从主函数断点上下文指针恢复上下文,切换到主函数。
当有任务就绪,再从主函数切换到任务,同时把主函数上下文断点保存到主函数断点上下文指针里。

我自己尝试写的voidtask就是这样调度的(没有空闲任务控制块)。

出0入0汤圆

发表于 2009-6-15 23:46:42 | 显示全部楼层
为了省空闲任务的堆栈空间?

出0入0汤圆

 楼主| 发表于 2009-6-15 23:55:36 | 显示全部楼层
节省堆栈空间只是其中原因之一,

还有个目的要与前后台系统结合起来。
至少给人错觉如此。


int main()
{
   …………
   OS_Start();
   …………

   while(1)
   {
      //空闲时,回到这里
    }
}


如果主函数while(1)再运行一个协作式事件调度,
个人认为是 抢占任务调度 与 协作事件调度 完美结合。

出0入0汤圆

发表于 2009-6-16 00:04:33 | 显示全部楼层
看来楼主对UCOS的了解还不够,OS_Start(); 是一个死循环,永远不会返回,你后面的while(1){}永远都会不会执行,就算你强行使用JMP指令跑到while(1){}里了,系统也会因为堆栈被破坏而崩溃

出0入0汤圆

发表于 2009-6-16 00:09:07 | 显示全部楼层
感觉还是按FFXZ说的  把stack回收了比较好...

出0入0汤圆

发表于 2009-6-16 00:09:33 | 显示全部楼层
15L理解才是不够...上官何许人也,这种溢出问题他会不知道?

跑到空闲任务或跑到main中的while(1)对于单片机而言,何尝不是一样的跑过去....
只不过主循环while(1)这个“空闲”任务处理上要特殊些倒是真的...

出0入0汤圆

 楼主| 发表于 2009-6-16 00:14:24 | 显示全部楼层
to 楼上:
OS_Start(); 是一个死循环,永远不会返回。
---------------------------------------------
代码改一下,就能让他返回。

你后面的while(1){}永远都会不会执行,就算你强行使用JMP指令跑到while(1){}里了,系统也会因为堆栈被破坏而崩溃
------------------------------------------------------------------------------------------------------------
为何要强行JMP,这显然不行。
实际上,记录一下主函数断点,让系统调度自动切换回来就行了。

我自己写的voidtask,作用相当于OS_Start();
void TSK_Start(void)                         //任务开始调度
{
   TTask *Task;  
   CRITICAL()
   {
      VTCurrentTask=NULL;                   //当前是NULL,即当前是Main
      Task=TSK_Get(&VTTaskReadyQueue);      //从就绪队列取出任务
   }
   TSK_SwapTo(Task);                        //切换到任务Task
}

// TContext *  volatile pContext1, * volatile pContext2;   
void TSK_SwapTo( TTask *ToTask)               //切换到任务Task(Task为NULL,回到主函数)
{
   TContext **   ppContext1, ** ppContext2;                    

     CRITICAL()
    {
            if(VTCurrentTask!=NULL)      
             {
                if(VTCurrentTask->Queue==NULL)
                {
                  obj_prio_put(&VTTaskReadyQueue,(void *)VTCurrentTask);  //使任务就绪
                }
               
               if(ToTask!=NULL )    //Task切换到Task
               {                     
                   ppContext1=&VTCurrentTask->pContext;
                   ppContext2=&ToTask->pContext;  
                   VTCurrentTask=ToTask;
                   CTX_Swap(ppContext1,ppContext2);
                 
               }
               else        //Task切换到Main
               {
                   ppContext1=&VTCurrentTask->pContext;
                   ppContext2=&VTpMainContext;
                   VTCurrentTask=NULL;
                   CTX_Swap(ppContext1,ppContext2);         
               }
             }
            else
            {
                if(ToTask!=NULL)   //Main切换到Task
               {           
                   ppContext1=&VTpMainContext;
                   ppContext2=&ToTask->pContext;  
                   VTCurrentTask=ToTask;
                   CTX_Swap(ppContext1,ppContext2);
                }
                else                      //Main切换到Main,不用切换
                {
                  
                }
            }
    }
}




移植UCOS;

void  OSStart (void)
{
    INT8U y;
    INT8U x;


    if (OSRunning == FALSE) {
        y             = OSUnMapTbl[OSRdyGrp];        /* Find highest priority's task priority number   */
        x             = OSUnMapTbl[OSRdyTbl[y]];
        OSPrioHighRdy = (INT8U)((y << 3) + x);
           
        OSTCBHighRdy  = OSTCBPrioTbl[OSPrioHighRdy]; /* Point to highest priority task ready to run    */
        
        OSTCBCur      = OSTCBPrioTbl[OS_IDLE_PRIO];
        
        CRITICAL()
        {
          OSRunning=1;
          OSCtxSw();
        }
    }
}

出0入0汤圆

发表于 2009-6-16 09:54:41 | 显示全部楼层
主函数,你能把它当作优先级最低的任务处理,那它也就只是一个任务
没什么对不对的
只要你能够这样调用。

你也可以把它做成一个优先级高一点的任务,完成你自己的编码。
这样,都不冲突,你还同样利用系统的统计这些功能,也能利用这个堆栈

我觉得这样更优吧

出0入0汤圆

发表于 2009-7-18 16:11:06 | 显示全部楼层
不懂。

出0入0汤圆

发表于 2009-7-21 11:35:24 | 显示全部楼层
感谢上官先生的思路和jathenal先生的解释,受益匪浅。感谢

本人对OS还是新人,有一些看法

1)关于main函数的不返回造成的堆栈浪费,我觉得可以通过调整堆栈大小的方法来尽可能的减轻一点。昨天看了这个帖子我特意看了一下keil 和iar 在编译cortex-m3的程序的时候都会给主堆栈(MSP)预留256个字节的大小。我昨天大概看了一下,如果初始化程序不是特别复杂(没有很深的函数调用),之前把中断都关了,可能也用不了太多的堆栈空间,我用的是zlg的一个ucos的模板,感觉进入OS_start()之前最多64个字节就够了。当然大家的程序都不一样,但是如果很在意ram的浪费,我觉得可以找一下进入os_start()之前最深的堆栈地址。这样浪费就比较少了。64字节对于16k的ram(lm1138)相当于1/256,我觉得用1/256的内存“浪费”换取“保证操作系统的良好结构,确保通用性、完备性和可移植性”还是值得的。个人感觉操作系统本身就是一个用ram和执行时间来换取多任务可靠执行的程序框架。

2)关于空闲任务的堆栈浪费,我觉得首先也是可以调整空闲任务的堆栈大小。但是我觉得空闲任务虽然什么都没有干,但是堆栈空间不算浪费因为他要承担着实时接收中断的任务,如果极端情况空闲任务运行的时候碰巧来了很多中断,那么空闲任务的堆栈空间非但不应该小反而应该大了。

不是很懂,一些个人看法,希望上官先生、jathenal以及各位高手指教。

aaa1982

出0入0汤圆

发表于 2009-7-30 09:35:54 | 显示全部楼层
关注高手的后续探讨!
学习了!
:-)

出0入0汤圆

发表于 2009-7-31 01:57:50 | 显示全部楼层
好像没必要继续了,看楼主一提问,只觉得应该是太热衷于前后台了.
OS相对于前代的初始化代码来说,一去不复返,你却想着他返回来进WHILE(1).呵呵,这样一来也不晓得该有多少漏子出来,至少统计任务是无法完成,CPU测速函数无法实现...............................

出0入0汤圆

发表于 2009-9-19 09:32:17 | 显示全部楼层
空闲任务的作用不一定就是简单的循环,可以是进行一定的事务处理。
不要因为ucosii的空闲任务是个简单的循环,就觉得空现任务是没用的。
还有很多其它的RTOS。多看看代码,就会明白了。

即使在ucosii里面,如果空现任务在运行,说明其它任务的事务已经处
理完成,或者是处于等待状态,那么运行所谓的“主函数”,也是在做
无聊的事务,并没用实际意义。如果这时“主函数”有事务处理,说明
系统并不是真正“空闲”。

其实呢,如果使用了动态存储器分配,虚拟存储器系统,空闲任务的一个
作用还有整理内存碎片,进行一些cache数据交换,把空闲内存区清零等,
并不是真正的什么也不做的“空闲”。只是ucosii简单化处理了而已。
大可以修改ucosii的空闲任务,做一些实用的工作。

出0入0汤圆

发表于 2009-9-19 09:53:38 | 显示全部楼层
在非常微型的调度内核,把主函数当空闲任务 个人看法没什么不妥。

基于这个想法,个人写了个voidtask,仅仅只是自娱自乐。
(个人没有任何RTOS用在实际项目的经验)

出0入0汤圆

发表于 2009-10-13 11:48:02 | 显示全部楼层
用主函数回收剩余CPU资源的方法适合于极简单的系统,属于代码技巧。不要在代码上过份投入精力,程序员才喜欢玩代码。

事实上从拓扑上说,你的主函数在这个时候其实就是一个变相空闲进程,却少了很多进程才能拥有的处理能力。正如你在楼上所说,只能自娱自乐。

出0入0汤圆

发表于 2009-10-16 14:26:26 | 显示全部楼层
个人觉得25楼的是正解,空闲并不表示CPU无事可做闲在那里,望文生意当然会出错。
每种OS的空闲函数都有适合自身的任务,有的甚至必不可少。贸然用主函数代替,有可能会导致系统崩溃。

出0入0汤圆

发表于 2009-10-28 11:24:44 | 显示全部楼层
mark

出0入0汤圆

发表于 2009-10-28 16:10:11 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-2-4 12:38:06 | 显示全部楼层
mark

出0入0汤圆

发表于 2010-2-25 14:55:46 | 显示全部楼层
main()函数使用的堆栈是可以使用独立中断堆栈的方式来回收利用的

大家可以看官方网站上for MSP430的移植,这是首次在uC/OS上实现了独立中断栈,大大减少了每个任务的堆栈需求,并允许中断嵌套,甚至可以在只有1K RAM的MCU上运行uC/OS

出0入0汤圆

发表于 2010-2-25 15:10:12 | 显示全部楼层
实时操作系统中设计空闲任务/线程,是因为当所有其它任务/线程都处在等待时,实时操作系统可以切换到空闲线程来执行,否则,操作系统中就没有可运行的任务/线程了(所有,在空闲任务/线程中,不能执行等待函数)。

    如果在主程序中不运行等待函数的话,是可以取代空闲任务/线程的。

    另外,我所看到的嵌入式实时操作系统中,几乎所有的实现中,main()函数只是个初始化函数,并不是主任务/线程函数,浪费了初始化main()时的堆栈空间。

    我在改造RL-RTX中,已经它main()函数作为主任务/线程来处理了。

出0入0汤圆

发表于 2010-2-25 15:20:20 | 显示全部楼层
有空闲任务很简单:
1。资源足够,不在乎MAIN函数那点堆栈
2。用空闲任务,OS更模块化,更安全(免得用户在主函数那里胡搞),用户接口更好。

出0入0汤圆

发表于 2010-2-25 15:31:48 | 显示全部楼层
开发者也想到了这个问题,所以给你提供了
void  OSTaskIdleHook (void) 函数接口

出10入95汤圆

发表于 2010-3-1 22:45:39 | 显示全部楼层
喜欢思考的上官!顶

出0入0汤圆

发表于 2010-3-5 14:29:01 | 显示全部楼层
楼主,你可以让主函数不要block,那不就一直运行在主函数了么??

出0入0汤圆

发表于 2010-3-5 14:54:02 | 显示全部楼层
我在系统里就是这么做的。原因在于,当中断发生频率非常高的时候,任务切换的代价急速上升,在这种情况下,当系统空闲的时候,就让CPU在核心里循环,等待中断,避免过于频繁的任务切换。

出0入0汤圆

发表于 2011-8-20 11:39:57 | 显示全部楼层
mark

出0入0汤圆

发表于 2014-10-8 11:07:13 | 显示全部楼层
新手MARK

出0入0汤圆

发表于 2014-10-8 11:12:41 | 显示全部楼层
好吧。。我只是用rtos,,没有详细去弄懂它的思想。

出0入0汤圆

发表于 2014-10-8 11:20:36 | 显示全部楼层
我能想到的就是省电了

出0入0汤圆

发表于 2015-3-26 13:57:48 | 显示全部楼层
为了省RAM,我曾经建立过2个任务。平时就在任务I跑,有事件触发,才到另外一个任务II。任务I就处理一些比如按键,LED等一些常规的事情,任务II就处理一些特殊的事情。任务I的优先级高一些。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-9 11:06

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表