ucos ii是怎么实现多任务运行的?很通俗易懂的描述

问题:
ucos上建立一个任务,格式如上图,它是一个死循环,但如果我建立了五个任务,并且五个任务里面没有延时,就只是像无操作系统那样写法,用死循环让它们一直跑,那这五个任务可以实现并行吗。



回答:

作者:不繆
链接:https://www.zhihu.com/question/55265639/answer/143727267
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

最近有用到ucosiii,跟ucosii还是有点区别,但是本质还是没有多大变化。一般跑ucos的都是单片机。如果是Soc基本上都跑linux了。这里我们要说一下基本知识,就是CPU的线程问题,像我电脑的CPU是i7的,四核八线程,所以他可以同时跑个8任务。而单片机只是单核单线程,所以每次只能跑一个任务,如果是裸机,那就是main()函数里面面的while(1)。那中断和定时器也是一样的不是意味着他可以同时执行中断任务和mian函数的任务,而是触发中断之后,暂停main函数中的任务,把当前的任务信息保存起来,然后去执行中断函数里面的任务,执行完了再恢复mian函数,从上次中断的地方继续执行。如果中断任务执行的时候,又有其他中断且是优先级比当前执行中断高的,那个这个中断会暂停,把当前任务状态保存起来,去执行那个优先级更高的中断,执行完成后再执行这个中断,然后再继续执行main函数。
所以,有人就想了这样一个办法,用定时器计时进行计数,然后把任务的总周期定下来,然后把时间分成好几份,分配给不同的函数,此时这每一个函数执行不同的功能,相同于执行不同的任务。这种做法应该叫状态机,不能叫操作系统。匿名四轴飞行器最早期的代码就是这样子写的,不过他全部代码放在中断里面执行,这样就不用自己写任务切换函数了,但是这个方法不是最好的方法,会有一些问题,中断函数应该不能写过于大的任务。
所以,现在的ucos之类的操作系统都采用了分时复用的方法,就是在不同的时间执行不同的任务函数。这个时钟由滴答定时器产生,一般为5ms中断一次,每次中断,都会去检查任务就绪表,如果有高优先级任务就绪了,就去执行他,然后把当前执行的任务保存在任务堆栈中,所以每个任务创建之前,都要先初始化一片内存用于存放当前任务执行的状态,如下面代码所示:
#define NRF24L01_STK_SIZE  128
OS_TCB NRF24L01TCB;
CPU_STK NRF24L01_STK[NRF24L01_STK_SIZE];
OSTaskCreate ((OS_TCB        *)&NRF24L01TCB,
	     (CPU_CHAR      *)"NRF24L01",
	     (OS_TASK_PTR    )NRF24L01_task,
	     (void          *)0,
	     (OS_PRIO        )NRF24L01_TASK_PRIO,
	     (CPU_STK       *)NRF24L01_STK,
	     (CPU_STK_SIZE   )NRF24L01_STK_SIZE/10,
	     (CPU_STK_SIZE   )NRF24L01_STK_SIZE,
	     (OS_MSG_QTY     )0,
	     (OS_TICK        )0,
	     (void          *)0,
	     (OS_OPT         )OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR,
	     (OS_ERR        *)&err);
这就是创建一个任务的函数,比ucosii多了好多参数。题主问这个问题是在奇怪为什么每个任务都是while(1),那还么去执行其他任务吗?其实关键就在这个滴答定时器的中断上面,因为只要进了中断,就跳出当前的while(1),然后我们就可以把当前的程序指针(PC寄存器)指向别的地方。其实每一个函数都存放在不同的地方,只要知道我下一个要执行的任务函数的首地址,那我只要把指针指过去就好了。可以看到上面初始中有一个:
(OS_TASK_PTR    )NRF24L01_task,
这个就是任务函数的地址。这个地址会保存在一个数据结构里面,这个叫什么我也不记得了,任务控制块?
所以你只要知道,单片机每次只能执行一个任务,多任务是分时复用来实现的,即ucos采用的方法。每个任务函数都是一个地址指针,要运行哪一个指令,就看PC寄存器指到哪。所以这就是为么要用滴答定时器的中断,因为我们可以在中断里面改变PC寄存器里面的这个地址,从而去到别的任务中,然后我们恢复那个任务上一次的执行状态,这就是为什么要开一个任务堆栈,就是为了存放任务执行的状态,包括各种变量的值。
<img src="https://pic4.zhimg.com/v2-2e0c1e0bf2d8427cbdd02fd74f0e063b_b.png" data-rawwidth="527" data-rawheight="540" class="origin_image zh-lightbox-thumb" width="527" data-original="https://pic4.zhimg.com/v2-2e0c1e0bf2d8427cbdd02fd74f0e063b_r.png">这个是ucosiii的任务状态转换图,只要你在while(1)任务函数里面调用了这些API,都会触发任务调度,例好OSTimeDlyHMSM()系统级延时函数,到了时间之后又会回到上次执行的地方开始执行。这里要说一点的就是,如果高优先级的任务一直不进行任务调度,那么该任务就会一直占用CPU,低优先级任务将一直无法执行。而最低优先级的任务即使没有进行任务调度,也会进行任务切换,因为滴答定时器每5ms进行一次中断,这个时候就会对任务就绪表进行扫描,如果有高优先级的任务进入了就绪状态,那么就会自动进行任务切换。所以后面就会涉及到一些加锁的行为,为了防止在运行的时候,时序被打断,下次回来就无法执行的情况。 这个是ucosiii的任务状态转换图,只要你在while(1)任务函数里面调用了这些API,都会触发任务调度,例好OSTimeDlyHMSM()系统级延时函数,到了时间之后又会回到上次执行的地方开始执行。这里要说一点的就是,如果高优先级的任务一直不进行任务调度,那么该任务就会一直占用CPU,低优先级任务将一直无法执行。而最低优先级的任务即使没有进行任务调度,也会进行任务切换,因为滴答定时器每5ms进行一次中断,这个时候就会对任务就绪表进行扫描,如果有高优先级的任务进入了就绪状态,那么就会自动进行任务切换。所以后面就会涉及到一些加锁的行为,为了防止在运行的时候,时序被打断,下次回来就无法执行的情况。
有错的地方请指各位知友指正。
  • 10
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值