打印

老生常谈---一种裸奔多任务模型

[复制链接]
楼主: dld2
手机看帖
扫描二维码
随时随地手机跟帖
21
forthlab| | 2008-6-23 21:11 | 只看该作者 回帖奖励 |倒序浏览

单片机上还是裸奔的多吧

很多应用的CPU只要4KROM,128RAM,没有办法上OS.我想国外的DX在类似的应用上也应该是裸奔的吧.

使用特权

评论回复
22
dld2|  楼主 | 2008-6-23 21:14 | 只看该作者

谢19楼找茬

不过韩国、日本,中科院、微软的,俺没那么大见识,不知从何说起。

试着谈一下19楼提到的问题:
对一个应用系统来说,我不认为可靠性与是否使用操作系统有多大关系。使用OS,不能防止死机、堆栈溢出、死锁、并发冲突等等问题。用好OS,更需要理论基础和实践经验。

windows是不是高可靠性的产品?我个人认为微软最成功的是商业模式,最大的功绩是推动PC和互联网的普及。

CPU的特权模式、软中断、原子操作等等,可说是专为OS提供的特性。似乎没什么好惊讶的。

使用特权

评论回复
23
IceAge| | 2008-6-23 23:14 | 只看该作者

少扯些政治,多谈些实际

对于大多数只有若干byte RAM 的mcu,os 是奢侈品,这已经不是 os 好不好的问题,而是既然无法使用标准的 os,那么何种方式的程序结构最佳的问题。

os 可以实现 task 的独立性,能够让程序设计“弱智化“,代价是任务切换时task 上下文的保护,这需要一定数量的 ram,以及 cpu 时间。对于多数 mcu 应用, 这不可接受。那么一种折衷的方法是: 裸奔(OSless) + multi-task.
每个task 不需要保护现场,由程序员设计时决定是否切换任务,程序员必须保证每个任务片没有死循环(os 没有此限制)或阻塞。完成这一过程最好的机制就是 ----- 状态机。用状态机把每个task 人为切片,每次执行一片,用固定顺序(rom 中) 或 task 表 (RAM 里Array, LinkList, Que)轮巡所有任务.

固定顺序:
Timer Interrupt: 
  call task1();
  call task2();
  ...
  call taskN();



一个简单的 task Array: 
typedef void (*FUNCTION_POINTER)();
FUNCTION_POINTER TaskArray[Array_Max_Count];

   for (i=0; i<Array_Max_Count; i++) { 
     FUNCTION_POINTER pTask = TaskArray;
     if (pTask != NULL) 
       (*pTask)();
   }  

剩下的事情就是如何用 state machine 分割各个任务。
    


 

使用特权

评论回复
24
IceAge| | 2008-6-23 23:29 | 只看该作者

补充:

task 运行放在中断里还是外,不是大问题。我个人喜欢全部放在定时中断里,主程序仅仅是一条休眠指令,好处是:1)低功耗 2)高可靠性,只要定时中断不被关闭,那么即便一个task 进入死循环,也能退出。要注意的仅仅是reset stack, 开放中断(对51,必须调用一次 reti) 

使用特权

评论回复
25
hotpower| | 2008-6-23 23:44 | 只看该作者

哈哈~~~支持老师!!!俺正在向零耗时任务切换迈进~~~

听起来像“永动机”,但俺感觉很好~~~

报告IceAge老师,俺进来迷上了汇编数组~~~

准备加入HotBIOS和FAT.

哈哈~~~

使用特权

评论回复
26
IceAge| | 2008-6-24 00:52 | 只看该作者

hotpower前辈真是精力充沛

再讨论一些 state machine 的一些技巧:

1)switch/case ,最常用的方法。每个时间片可以通过 状态索引 切换状态
   switch (state) {
     case 0: Initialize();break;
     case 1: state1(); break;
     case N: stateN(); break;

void Initialize(){ state++; } 
void state1() { state++; } //下一时间片运行下一个状态
void state2() { count = 100; state+=2;} //下一时间片运行下两个状态
void state3() { state--; } //下一时间片运行上一个状态 
void state4() { count--; if (count == 0) state++; } //delay 100*tick
         
2)静态函数指针 + 状态索引。程序更通用简洁
ROM 中建立
FUNTION_POINTER functions[] = { Initialize, state1, state2, ... stateN }; 
void TaskN() { (*functions[state])();}

3)动态函数指针; //不推荐, 看看就行了
void task(FUNCTION_POINTER* pTaskPtr) 
{ (**functions)(pTaskPtr); }
void state1(FUNCTION_POINTER* pTaskPtr) { *pTaskPtr = state2; }
 

使用特权

评论回复
27
hotpower| | 2008-6-24 01:22 | 只看该作者

哈哈~~~俺裸奔从来不在主循环里做事,最多来个中断隐身效果

使用特权

评论回复
28
hotpower| | 2008-6-24 01:41 | 只看该作者

实际上俺专门搞了个C++裸奔栏目,现在穿衣做HotBIOS嫁衣~~~

哈哈~~~00都有OS,俺也不能落后呀~~~虽然俺裸奔"很有特色"~~~

穿衣很累~~~后悔目标定的太高---直接挑战TI大鼻子老外~~~

国内俺不敢~~~因为都是"阶级弟兄"~~~都是俺的老师---包括00同学~~~
相关链接:http://blog.**/hotpower/24497/category.aspx

使用特权

评论回复
29
hotpower| | 2008-6-24 08:00 | 只看该作者

这次菜农如此努力就是要证实---中国的农民也能OS~~~

哈哈~~~以后不要总认为自己的水平有多高~~~

要记住一个要退休的农民也能"OS"~~~


人上人,天外天...

使用特权

评论回复
30
dld2|  楼主 | 2008-6-24 08:21 | 只看该作者

好好!

大虾出手,开阔思路。

使用特权

评论回复
31
gyt| | 2008-6-24 09:29 | 只看该作者

支持菜农

使用特权

评论回复
32
yuchole| | 2008-6-24 11:51 | 只看该作者

请教IceAge大侠

看了你上面的回帖,有几个地方不是很清楚
用状态机把每个task 人为切片,每次执行一片。
这里的的切片只指把每个task分成不同的时间片吗?我的意思是在每个时间片里,是执行一个完整的task,还是只执行一个task的一个state?
如果是将每个task都分成不同的state的话,多任务又是怎样实现的呢?
希望各位大侠能指点一下,多谢了。

使用特权

评论回复
33
农民讲习所| | 2008-6-24 14:13 | 只看该作者

俺从来都是放在主循环中的

中断都是用来实现任务的实时处理的。经过大量实践,充分证明了这种观点。
OS用中断实现调度,再怎么说也牺牲了中断的实时处理特性,这样的牺牲在MCU上是很不值得的。前后台的特性非常适合MCU,用主循环中的任务转移实现了OS的任务调度特性,是非常非常划算的做法。

加入低功耗处理是非常简单的事情,只要关注两点:中断、消息队列是否空。比用中断实现的要容易百倍。

凡是OS用中断去调度的,俺认为都不适合MCU。

使用特权

评论回复
34
农民讲习所| | 2008-6-24 14:19 | 只看该作者

一个运行中IDLE的例子,只要丢到主循环都能实现。

sSysTimer.bSysTouch是系统20MS产生的一个全局标志,和消息是等价的,和消息比较是所有任务模块都可以直接使用该标志。也是LZ所提的TICK。

//-------------------------------------------------------------
//运行中空闲模式
//-------------------------------------------------------------
#include <libRain_Base.h>
#include <libRain_M169.h>

//-------------------------------------------------------------
//省电模式
//-------------------------------------------------------------
void InSysIdle_Idle( void )
{
    U8 mTemp;
    
    mTemp = TIMSK0;
    TIMSK0 |= ( 1<<OCIE0A );
    SMCR = (1<<SE) | (0<<SM2)|(0<<SM1)|(0<<SM0);    //空闲模式
    __asm__ __volatile__("SLEEP");
    
    TIMSK0 = mTemp;
}

//-------------------------------------------------------------
//运行中空闲
//消息空且系统时间未触发,进入。中断退出。
//-------------------------------------------------------------
void SysIdle_Loop( void )
{
    if( ( sMsg.aReadMsgBuffer[0] == 0 ) && ( !sSysTimer.bSysTouch ) )
    {
        //无消息发出, 无系统时间触发,无SPO2检测定时器,开始进入空闲模式
        InSysIdle_Idle();
    }
}

//-------------------------------------------------------------
//T0中断
//因系统时间使用T0查询,退出省电模式需中断,故有此函数.中断产生不用.
//-------------------------------------------------------------
SIGNAL( SIG_OUTPUT_COMPARE0 )
{
    sSysTimer.bSysTouch = true;
    TIMSK0 &= ~( 1<<OCIE0A );
}

使用特权

评论回复
35
wxj1952| | 2008-6-24 14:25 | 只看该作者

故意找茬。

“在Windows95/98中,如果某一个程序运行出错,则整个系统不能继续运行。这个缺点在Windows NT和Windows XP系统中得到了解决,这是因为新的系统将各个进程的空间完全隔离,如果一个进程崩溃,其它进程不受影响。”

注意以上是《汇编语言程序设计》的教材上的内容。不直接说明或许看不懂,它背后的含义是Windows NT和Windows XP并不是依靠全软的编程方法解决问题的,它依赖于新型CPU和相应发展、扩展的汇编语言。

裸奔,就好像你用一个具有特权级、任务、中断及异常等功能的CPU,——它能够简单地实现任务之间的隔离,轻易地使系统的坚固性得到提升——将这些功能弃而不用,却一定要把它设置为实模式下编程,只使用20年前的传统教材方法进行软件设计......

其实,裸奔和OS的最大区别,是OS设计模式的可靠性。谁一定要说,我裸奔做得比OS还坚固可靠,那就好像是说“我把Windows98优化了,运行比Windows XP更坚固(robust)。”
如果从不看书,只独立思考,只顾自己的美感裸奔。那一定会认为:
    “ ——在中断中处理任务,是个不错的主意。进入中断时,首先CALL RETI把自己改变成子程序,即使崩溃,系统心跳仍然会重新启动系统。”

岂不知多本书上都写过,这是可靠性设计大忌。将会产生什么后果,自己看书...。先看看已经成为规范的文字:
A、高特权级的程序不能直接调用或跳转(CALL或JMP)到低特权级的程序......
B、
(再看看“为什么需要一个‘刷新函数’”)
中断程序是最高特权级0的系统程序,用户程序是低于任何系统程序的特权级3。谁可以调用谁?为什么要这么规定?弄清楚所有这些规则——以及违背规则将会产生的后果——之后再来谈裸奔方法好么?

千万别说,咱什么书都不屑一看,天天自己创新。

使用特权

评论回复
36
农民讲习所| | 2008-6-24 14:33 | 只看该作者

LS说的都是OS构成的基本要素之外扩展出来的

是OS的扩展,不是OS的基础。MCU明显不能使用这类扩展特性。

使用特权

评论回复
37
wxj1952| | 2008-6-24 15:25 | 只看该作者

支持所长。

看了一下51上的OS源代码,发现它们都是按照这些基本规则框架设计的。就是说MCU的OS虽然有部分特殊性,但是OS基本设计原理,大家都是一样的。

如果看不懂51的OS源代码,参考任何一本《计算机操作系统教程》都可以。

所长未看出我言外之意,像“高特权级的程序不能直接调用或跳转(CALL或JMP)到低特权级的程序......”这样的规范,其实早在专用MPU出现之前就有了。应该说,是现代CPU在遵循规范设计硬件电路,而不是有了现代CPU之后才制定的规范。

您看ucosII中什么时候使用CALL,什么时候使用JMP,什么时候可以快速切换堆栈,什么时候必须PUSH,以前以为是设计者的巧妙思维,结果突然发现根本早就有了规范文本,是按照模式框架填充内容呢。(rtx51也一样。)
再看RTX51中的Kernal和Switching之间的关系,根本不是想(奔)出来的。“内核不能调用任务。”即使是没有特权指令的8051_OS.

中国的教材上只能教人,这段程序是什么意思,那条语句的意思,只能教人怎么写程序,却不能说出,为什么要这样写。

有篇**《曹冲为什么不能成为阿基米德》,曹冲聪明裸奔了一辈子,也没总结出个浮力定律。结果后人还得不断地采用换石头的方法称重物。几百年、几千年也没奔出个定律来。

使用特权

评论回复
38
machunshui| | 2008-6-24 16:06 | 只看该作者

就写程序而言

就写程序而言,和老外的高手比,

国人的差别是数量级上的差别.

使用特权

评论回复
39
农民讲习所| | 2008-6-24 16:18 | 只看该作者

ls这样说恰当写

就写程序的管理而言

使用特权

评论回复
40
machunshui| | 2008-6-24 16:50 | 只看该作者

恰恰相反!

"而外国的设计师不是艺术创新能力怎么样,而是他看得多,知道前人众多大师是怎么干的,他学(仿)得好,他“积木”搭的好,不是他自由创新的好。"


恰恰相反!

看看中外绘画的学习方法就知道,

老外的叫写生,中国的叫临摹.


老外厉害在追根求缘,干事情遵循科学,即在公理和定理的基础上干事情,一切都讲究完美.

国人讲究变通,达到目的就行,实际上更加现实.


所以中国的哲学发展到最后成了儒学等社会学,中国的炼丹术永远是炼丹术.

老外的哲学最后发展成了科学,练金术最后发展成了化学.

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则