发新帖本帖赏金 1.00元(功能说明)我要提问
1234下一页
返回列表
打印

嵌入式实时系统的理解

[复制链接]
20824|79
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
1026869700|  楼主 | 2016-10-4 00:05 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
     距离上次发帖,已经过去有差不多半年了,最近又到了自己半年一次的总结了。首先说说自己的编程风格的变化,在上一篇中提到的数据结构、配置文件结构、预编译结构,目前已经逐渐适应,也在逐步加深应用。可以说是这半年的一点小变化吧。
    在这中间呢,又跑去折腾了uCOSII和freeRTOS,uCGUI,C#,Linux,ARM9等乱七八糟的东西;现在呢,还是安心的继续搞搞STM32和C语言吧,在飞之前,还是要经历走和跑的过程的。
     今天在这里说说我对uCOS和freeRTOS的一点小理解吧。只是针对这两种实时系统的结构和编程思想,不牵扯准确的应用。主要是对新手学习之前有一个结构上的理解。然后呢,本人在嵌入式实时操作系统方面也是一个很老的菜鸟而已,有什么遗漏和不对的地方还希望大家能多多包涵。
--------------------------------------------------------------我是分割线--------------------------------------------------------------
     正文时间到,学习了那么久实时操作系统,对于操作系统的结构认识也是很模糊的;而去阅读源码吧!是一个很痛苦且工作量很大的事情,本人也是折腾N久,终于有一次(也就是在折腾ARM9时的意外收获)发现一片优秀**,完后才把实时操作系统基本的结构组织了起来。想想对新手是有很大的帮助,也就来写出来。在这里再次强调一句老掉牙的话:要会一点汇编、了解单片机运行原理和对C有较好的理解。
      其次呢!单片机它只有一个CPU(排除那个什么双核的单片机,其实我也不太明白它),也就是说它一个时刻点只能做一个事情,就像让你同时照顾10个婴儿一样,你也只能是先哄哄这个,再去哄哄那个。单片机的多任务也是一样的原理。
-------------------------------------------------------------我还是分割线------------------------------------------------------------
    首先说说一般的、最简单的单片机程序:
   void main(void)
  {
        XXX_Init()
        while(1)
       {
              xxx();
              xxx();
             ......
       }
  }
  好了,我刚开始学51的时候,也是这么写的,其实大家都是这么写的。当然这个还没有包括中断。如果加上中断呢?其实能琢磨RTOS的我想基本都会用中断了吧!但是能理解本质的不一定是全部。我还是画结构吧。
    void main(void)
  {
        XXX_Init()
        interrupt_init();
        while(1)
       {
              xxx();
              ----------------------->假设中断发生在这里,那么单片机就要掉头了,去执行中断了-------->
              我胡汉三又回来了<-------------做好回来的准备<--------------------我从中断忙完了<---------|
              xxx();
             ......
       }
  }
    好了,大概就这么个样;其实当时我在想,估计多任务操作系统就是根据硬件中断原理写的吧!
    记住硬件中断的准确步骤,有用的。我尽可能的写的通俗一点。
    单片机在运行的时候,用到的几个核心的寄存器(写过汇编的人明白)就是PC、累加器和几个CPU的Rx寄存器、在C语言中还有SP(架构不同,细节不同,基本一样)。在正常运行时,用到了这几个寄存器,我叫它们公用寄存器;那么中断发生时,是要进中断函数的,在中断函数里面也是要进行各种运算的,也会用到那几个公用寄存器,怎么办呢?这个时候,用到了C语言里面的那个栈,把那些东西先暂时保存到栈里面(入栈)(结构不一样,栈的理解也不一样,不细说);在中断服务函数里面,就可以把那些公用寄存器值覆盖掉,而去填入新的值。在中断函数执行完毕后,再把那些数填回到那些公用寄存器(出栈),这样就可以实现while(1)主线程和中断辅助线程的双任务了,多个中断也可以排列成优先级不一样的多任务。
     而实时操作系统呢,就是类似于用软件来产生单片机硬件那样的中断,而且那中断是可控制的。单片机的运行,实际就是PC指引着CPU去哪儿执行命令,CPU用RX和累加器来就运算中间缓冲。操作系统实际就是用软件控制了PC的值,而不是那种单纯的靠着PC指针自加去顺序的执行命令。
     说到这儿,通俗的原理性的东西好像写完了,剩下的就是软件上面的猛料了。现在回想起来,觉得那些结构和思路写的太完美了。半夜了,等明晚这个时候写那些在软件上实现任务转移的步骤分析步骤。
     

打赏榜单

yyy71cj 打赏了 1.00 元 2016-10-23
理由:赔偿你没糖的损失

评分
参与人数 3威望 +18 收起 理由
刘东君 + 4 很给力!
长的帅怪我落 + 4 赞一个!
yyy71cj + 10 版主终于大显神威了,好好分享你的气场…….

相关帖子

沙发
yuanquan12345| | 2016-10-4 08:15 | 只看该作者
学习。

使用特权

评论回复
板凳
gszou| | 2016-10-4 09:05 | 只看该作者
楼主说的没错,但是这是结果。我个人感觉这说话会引人入局的。
事情常常是有“因”才有“果”的,我觉得要理解操作系统,还是从根源(因)说起,也就是说清楚一个CPU如何实现多任务呢?
当思考着如何写这样的程序时,就会很容易引入操作系统。
反之,先说中断,切换什么的,那找不着边了。

使用特权

评论回复
地板
1026869700|  楼主 | 2016-10-4 12:36 | 只看该作者
gszou 发表于 2016-10-4 09:05
楼主说的没错,但是这是结果。我个人感觉这说话会引人入局的。
事情常常是有“因”才有“果”的,我觉得要 ...

     其实呢,我的想法是,先说说单片机的硬件中断(其实很多人是明白的),然后再去讲多任务是如何切换的,考虑到直接讲多任务切换,有很多新手会云里雾里,不知道为什么要那样做;而硬件中断只是我想做的一个铺垫。为了说明在切换任务时,必须要做的几个基本事情。

使用特权

评论回复
5
coderdd| | 2016-10-4 21:35 | 只看该作者
楼主继续,说的不错

使用特权

评论回复
6
1026869700|  楼主 | 2016-10-4 23:41 | 只看该作者
  接着昨晚的说吧,可能会有有些跳跃,但是会循着前面的框架是不变的。
      先说说任务吧,一般呢,在嵌入式操作系统里面,可以叫任务,也可以叫线程,我在底层习惯叫任务,在上位机编程时习惯叫线程。而嵌入式系统多任务编程,它的结构有点不一样,因为啊,每个任务都是一个死循环,在系统运行开始后,操作系统会接管CPU和硬件资源。
      void main(void)
      {
              xxx_init();
              yyy_init();
              OS_Init();
              OS_Creat_Task(Task_A, ***, ***, .....);      //注册任务A;
              OS_Creat_Task(Task_B, ***, ***, .....);      //注册任务B;
              OS_Creat_Task(Task_C, ***, ***, .....);      //注册任务C;
              OS_Start();
      }
      带系统的主函数结构就长那个样。
      而那些任务函数呢?长什么样?
      void Task_A(void)     //想带参数也可以啊,这里不讲那个。
     {
           ***_Init();
           while(1)              //此任务所做的事情,就在这这个循环里面了。
          {
                   ******();           
          }
      }
     任务函数,基本都长这样;在这里说说,为什么每个任务都是一个死循环呢?因为啊,任务的切换,必须是受系统管理的,如果系统没有实现调度切换,那么硬件自己是无法实现切换任务的。就这个理!还有一个是叫做什么程序的局限性什么的,记不清名称了。
     而上面的那个OS_Creat()函数呢,就是把任务添加到系统的任务表里面,最后那个OS_Start()是开始系统调度,系统会把PC指针指向优先级最高的那个任务的函数所在的地址,CPU就接着那个地址开始执行那个任务。在这里呢,还要说一个很重要的东西:任务的独立性,也就是说,任务对任务之间是相对独立的、封闭的。一个时刻只有一个任务在占有CPU,那么如何让一个任务独立呢?我们来分析一下,一个任务就是一个函数,函数里面就是局部变量和计算,还有函数调用,函数调用其实就是在编译时,已经成为了PC指针的转变了。而前面讲的那个任务的切换要入栈,把公用寄存器保存了,那么局部变量呢?C语言中的局部变量是定义在栈中的,全局变量不牵涉到私有性。如何让任务独立呢?在这里,使用私有栈,C函数的运行是需要栈和公用寄存器的,对每个任务构造一个私有的栈,那么这个任务就独立了。
     也就是说,系统的任务切换实际就是控制从一个任务的while(1)转移到另一个任务的while(1)里面,最快的方法就是转移PC指针,一步到位。可以对照前面那个硬件中断原理;在主函数的while(1)里面执行命令,发生中断(任务切换),转换到中断服务函数(进入新任务),在中断发生时,硬件自动将公用寄存器入栈,然后转换PC指针。出栈时,硬件自动将栈里面的数据还原回去。
     没有系统时,也就只有一个任务,因此一个公共的栈就够了。在多任务时,就不行了,每个任务是不能去访问别的任务的栈的,会出乱子的。也就是前面说的,任务要独立。说到那个私有栈的事情,那么问题来了,如何为一个任务分配一个私有的栈呢?我们仔细想想,栈是如何访问的?栈是通过SP访问的,SP也就是栈指针。我们人为的为一个任务定义一个栈顶地址和栈的长度,在任务调度切换时,把分配的这个栈地址通过底层传递给SP。因为栈是不会跳跃的,只会一步一步的增长,或者退栈。
     到了这一步,我想需要整理一下任务是如何切换的。
     1、系统开始调度,取出任务就绪表里面优先级最高的任务。
     2、将这个任务栈里面的SP、PC、RX等公用寄存器的值还原回去(假设这个任务之前已经被运行过;没运行过的话,只需要更改SP和PC就行了)
     3、进入新任务运行。
     4、任务运行时间到、重新进行任务调度。
     5、取出新任务的数据,填入寄存器。
     6、执行新任务。
     7、循环到4.
    任务之间结构就这样。但是程序里面还细化很多东西的,也就是每个任务的参数的管理。要记住,每个任务针对CPU在运行时都有一组数据,那么任务切换时,要保护好当前任务的运行环境,然后再切换,切换进入新任务之前,是需要将新任务之前离开的CPU运行环境恢复出来,任务才能恢复接着运行。
    在系统里面,有一个叫做TCB的东西,这是什么东西呢? Task Contrl Block:任务控制块。在这里,为每个任务定义一个叫做任务控制块的结构体,这个结构体保存对应任务的运行环境,这个TCB里面有基本的任务栈地址、运行环境等参数。系统对任务的管理,实际上就是对任务控制块的管理。
    进一步分析一下任务的切换:
    1、从就绪表里面找出优先级最高的任务;
    2、通过优先级,从TCB数组里面找出相应任务的任务控制块;
    3、通过任务控制块,找到任务私有栈的地址;
    4、通过任务私有栈,恢复任务的运行环境;
    5、转移PC指针到新任务的运行处。
   到这里,任务切换的进一步分析就结束了。
   明天讲一下抢占式内核和任务里面的延时处理。

使用特权

评论回复
7
gszou| | 2016-10-5 13:07 | 只看该作者
1026869700 发表于 2016-10-4 12:36
其实呢,我的想法是,先说说单片机的硬件中断(其实很多人是明白的),然后再去讲多任务是如何切换 ...

不同人,不同的表达方法,不同的思路。我支持你说下去。

使用特权

评论回复
8
qianshuyong21| | 2016-10-5 14:30 | 只看该作者
Good! 继续,关注中

使用特权

评论回复
9
1026869700|  楼主 | 2016-10-5 22:18 | 只看该作者
yyy71cj 发表于 2016-10-5 18:34
士别三日当刮目相看

总归是要成长的。

使用特权

评论回复
10
1026869700|  楼主 | 2016-10-5 23:36 | 只看该作者
    说说今天的新话题,“抢占式内核”,“任务延时”。
    什么是抢占式呢?在做STM32的中断的时候,里面有个对中断优先级分组的概念,就有关于这个抢占优先级。对于做51的朋友来说,可能不了解这个概念,其实呢!也不是很难,因为51里面就是抢占式中断方式,也就是说只要发生中断优先级更高的,就可以打断优先级低的,这就是抢占;那么抢占了什么呢?自然是抢占了内核、抢占了CPU的使用权。
    在这里综合说一下这个uCOSII和freeRTOS,uCOSII是支持抢占式功能的,开始的freeRTOS是不支持的,现在的加入了抢占式内核的功能。那么什么是怎么抢占内核呢?在uCOSII中,它的任务调度切换有两个基本的使能,第一是时间片结束;第二是有优先级比当前任务高到就绪任务。时间片切换时正常切换,而出现优先级高的就是属于抢占式切换。在freeRTOS,一般使用的是时间片切换。
    当然,还有一个东西要放在这里讲,就是任务的延时。即使是多任务编程,单个的任务有时还是需要延时或者等待的。至于延时什么,或者等待什么。大家都各有各的理解。用比较官方的话来说,就叫做:等待资源。在嵌入式实时操作系统里面,这里的延时是有讲究的;怎么个讲究法呢?为了使CPU能高效的工作,系统提供了通用延时函数,这个延时函数在调用时,它会把当前任务挂起至等待状态,而去执行其它任务,当系统节拍计时时间到,系统会再次回到那个延时函数处,继续执行任务。
    我好想把一个很重要的东西给忘记说了。系统的滴答时钟,这个就是操作系统的心脏一般的结构。用它来给任务划分时间片的,在它的中断服务函数里面执行一些系统的很重要的任务。我只讲结构和思想,至于具体执行什么,自行深入了解。滴答时钟的时间片到了,那么这个时候任务必须要放弃(被剥夺)CPU使用权。还有一个要注意的就是,在抢占式内核的情况下,任务里面必须且最少要有一个系统的等待或者延时函数。这是为什么呢?。。。。。。我想卖关子了...如果在高优先级任务里面不使用等待或者延时,那么出现的情况就是高优先级的会永远占着CPU,因为除了时间片,没有什么能剥夺它的CPU使用权,即使时间片剥夺了它的CPU使用权,让它进入任务就绪表,那么他很快又能把CPU抢回来。这样导致低优先级的一些任务分不到CPU使用权。
    说到这儿,抢占式内核的框框好像说完了,延时的框框也说完了。还有时间,在说一下硬件中断和临界资源的访问吧。
    说到这个硬件中断,我起初以为是把硬件中断给和谐掉了,或者做特殊处理了。当分析完之后,发现真是被特殊处理了。怎么个特殊处理法呢?说说我看到的freeRTOS里面是如何做特殊处理的。freeRTOS呢,在移植的时候,是需要改.S文件里面的三个中断入口函数名的。起初在想,为什么呢?后来明白了,它是为了让操作系统把硬件中断做统一管理。怎么个统一管理呢?这里先说说别的吧,至少硬件中断的功能还是在的,而硬件中断的处理是在系统之内又在系统之外的一个东西,因为系统也不知道那丫的什么时候会来。本来系统这边正常任务调度呢,它来了还要赶过去处理它,话说如果在正常执行任务时,来硬件中断了,那就按照和单任务的切换差不多,关键是如果刚还出现在任务调度的时候,那么麻烦了。还有一种情况就是,在处理中断呢,结果有高优先的任务就绪了;那么就要在中断函数里面将任务切换掉,就形成了,从A门进的中断,在出中断的时候,发现自己是从C门出来的,就这种感觉。接着说那个个统一管理中断的事情。不管在任何时候,只要中断来了,CPU就会保护好当前现场,转到中断去执行。软件里面把中断托管到了一个函数,也就是说,不管发生什么中断,都进那一个函数,这样在这个函数里面,处理好转移到中断的一些事情,接着跳转到对应的中断服务函数。做了什么事情呢?说一个最简易的吧,就是这个管理函数进来了几次。完成一个服务出去时,递减一下。就是为了计算中断嵌套了多少层,当那个检测到所有中断都全部退了了,再离开这个中断管理函数。离开之前要做的就是检查一下在执行中断这段时间里面,有没有发生任务切换,如果发生了,那么就不能按原路返回了,要把回去的路改一下,直接回到优先级高的那个任务。也就是说在退出中断的时候,顺便把任务也给切换了。(中断这里的东西有点多,我也不明白怎么讲才能清楚一点,有什么疑问可以留言,我直接解答)。而中断服务函数呢,和一般的写法完全一样,这里就只是对中断服务的入口做了个特殊处理。
    中断那里就讲上面那样,感觉那里重点很多,不知道怎么精简讲。
    这里我说说临界资源吧!(怎么感觉让我把嵌入式实时系统给肢解一样,一块一块的,而且不带关联的)
    临界资源,什么是临界资源了,就是这个东西不光是属于当前一个任务的,还可能关联到其它任务。举个形象的例子:当前正在访问一个变量,刚把变量读过去,结果这个时候发生了任务调度,把任务给切换走了,在别的任务里面,又把这个变量给改写掉了,当你在回来时,已经不是当年那个夏雨荷了,成了容嬷嬷了。临界资源是一次仅允许一个任务使用的共享资源,每个任务中访问临界资源的那段程序称为临界区。在执行这些代码的时候,要禁止发生调度,因此在嵌入式系统编程里面,凡是要访问临界代码之前,都有调用一个函数,就是这个OS_ENTER_CRITICAL()  ,这个时候就可以尽情的访问了,没人打扰你。在访问结束后要退出临界访问OS_EXIT_CRITICAL() 就这个函数。退出后,系统就可以进行正常调度了。
    今晚就讲到这里。写的太多担心大家没耐心看。明晚写内存分配。和数据访问。

使用特权

评论回复
11
yuanquan12345| | 2016-10-6 08:24 | 只看该作者
**跟着楼主看下去,虽然不懂。

使用特权

评论回复
12
1026869700|  楼主 | 2016-10-6 09:56 | 只看该作者
yuanquan12345 发表于 2016-10-6 08:24
**跟着楼主看下去,虽然不懂。

谢谢关注,你有哪里不懂得可以说说出来。我也知道,越是到后面,重点越多,越难理解。

使用特权

评论回复
13
yuanquan12345| | 2016-10-6 15:40 | 只看该作者
1026869700 发表于 2016-10-6 09:56
谢谢关注,你有哪里不懂得可以说说出来。我也知道,越是到后面,重点越多,越难理解。 ...

不用理我,我就是一个门外老头,喜欢看这个。

使用特权

评论回复
14
Simon21ic| | 2016-10-7 01:26 | 只看该作者
本帖最后由 Simon21ic 于 2016-10-7 02:12 编辑

LZ觉得实时操作系统和普通状态机构架都有什么优缺点?

实时系统应该如何定义?
满足应用的实时性要求的系统?

LZ只是说了抢占式的任务切换,其实很多RTOS还支持协程(合作式独立堆栈)甚至状态机(共享堆栈)。
不同的任务实现方法,其实只是各种不同资源以及成本的权衡而已。
LZ应该对抢占式很了解了,可以看看我的VSF构架,相信如果能够看明白的话,会发现另外一种设计思路。
VSF短短几百字节的内核中,就包含了抢占式(事件队列之间的抢占,可以独立堆栈也可以共享堆栈),协作式独立堆栈(setjmp/longjmp方式),以及共享堆栈(事件驱动以及PT线程)。

使用特权

评论回复
15
msblast| | 2016-10-7 12:51 | 只看该作者
Simon21ic 发表于 2016-10-7 01:26
LZ觉得实时操作系统和普通状态机构架都有什么优缺点?

实时系统应该如何定义?

你的内核在哪呢?想了解下。。。

使用特权

评论回复
16
1026869700|  楼主 | 2016-10-7 14:45 | 只看该作者
Simon21ic 发表于 2016-10-7 01:26
LZ觉得实时操作系统和普通状态机构架都有什么优缺点?

实时系统应该如何定义?

    其实发这个贴的本意不是要对实时操作系统做深入了解的,只是为了让新手对实时操作系统能有一个框架上的理解,明白主要模块的具体功能。开头就说了,本帖是给想学习实时操作系统的新手准备的。我本人倒是也很想看看你的VSF架构的。方便发出来看看吧?本人现在写软件也只是业余的事情,工作不牵扯任何软件。

使用特权

评论回复
17
1026869700|  楼主 | 2016-10-7 14:53 | 只看该作者

大版主,你现在主要做什么?是不是搞STM32了?发现没什么能挡住你的脚步,总是勇往直前。哈哈哈

使用特权

评论回复
18
1026869700|  楼主 | 2016-10-7 16:12 | 只看该作者
yyy71cj 发表于 2016-10-7 15:40
对于数字技术,已经没什么能难住人的了,高手寂寞如斯,奈何英雄无用呼? ...

渴望成为你这样的高手。

使用特权

评论回复
19
Simon21ic| | 2016-10-7 18:11 | 只看该作者
yyy71cj 发表于 2016-10-7 15:40
对于数字技术,已经没什么能难住人的了,高手寂寞如斯,奈何英雄无用呼? ...

厉害啊,果然是高手

使用特权

评论回复
20
Simon21ic| | 2016-10-7 18:13 | 只看该作者
msblast 发表于 2016-10-7 12:51
你的内核在哪呢?想了解下。。。

GitHub.com/versaloon/vsf
核心在这里:
https://github.com/versaloon/vsf/tree/master/vsf/framework/vsfsm

使用特权

评论回复
发新帖 本帖赏金 1.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

15

主题

138

帖子

15

粉丝