> 文档中心 > RTOS的基本概念与线程基础知识

RTOS的基本概念与线程基础知识

1 RTOS概念及线程的引入

1.1 RTOS的概念

用人来类比单片机程序和RTOS:


妈妈要一边给小孩喂饭,一边加班跟同事交流,怎么办?
对于单线条的人,不能分心,不能同时做事,她只能这样做:

  • 给小孩喂一口饭
  • 瞄一眼电脑,有信息就去回复
  • 再回来给小孩喂一口饭
  • 如果小孩吃这口饭太慢,他回复同事的信息也就慢了,被同事催,你半天都不回复我?
  • 如果回复同事的信息要写一大堆,小孩就饿的大哭起来

对于眼明手快的人,她可以一心多用,她这样做:

  • 左手拿勺子,给小孩喂饭
  • 右手敲键盘,恢复同事
  • 两不耽误,小孩“以为”妈妈在专心喂饭,同事以为“她在专心聊天”
  • 但是脑子只有一个啊,虽然说一心多用,但是谁能够同时考虑两件事?
  • 只是她反应快,上一秒钟在考虑夹哪个菜给小孩,下一秒钟考虑给同事回复什么信息

这种做法,在软件开发上,就是使用操作系统, 在单片机里叫做使用RTOS

RTOS的意思是:Real Time Operating System,即实时操作系统,但使用Windows,我们经常碰到程序卡死、停顿的现象,日常生活中,这是可以忍受的,但是在电梯系统中,你按住开门键时如果没有即刻反应,即使知识慢个一秒钟,也会夹住人,在专用的电子设备中,实时性很重要

1.2 程序简单示例:

//经典单片机程序void main(){    while(1)    { 喂一口饭(); 回一条消息();    }}//RTOS程序int a;喂饭() 栈A{    int b=2;    int c;    c = a+b;==>1.b+2,2,c=new val    --------------------------->切换    while(1)    { 喂一口饭(); }}回信息() 栈B{    int b;    while(1)    { 回一个消息();    }}void main(){    create_task(喂饭);    create_task(回信息);    start_scheduler();    while(1)    { sleep();    }}

 1.2 提出问题

什么叫线程?回答这个问题之前,先想想怎么切换线程?怎么保存线程?

  • 线程是函数吗?函数需要保存吗?函数在Flash上,不会被破坏,无需保存
  • 函数里用到的全局变量,全局变量需要保存吗?全局变量在内存上,还能保存到哪里去?全局变量无需保存
  • 函数里用到了局部变量,局部变量需要保存吗?局部变量在栈里面,也是在内存里,只要避免栈被破坏即可,局部变量无需保存
  • 运算的中间值需要保存吗?中间值保存在哪里?在CPU寄存器里,另一个线程也要用到CPU寄存器,所以CPU寄存器需要保存
  • 保存在哪里?保存在线程的栈里面
  • 怎么理解CPU寄存器,怎么理解栈?

2.1 ARM架构及汇编

ARM芯片属于精简指令集计算机(RISC:Reduced Instruction Set Computor),它所用的指令比较简单,有如下特点:

1、对内存只有读、写指令

2、 对于数据的运算是在CPU内部实现

3、 使用RISC指令的CPU复杂度小一点,易于设计

对于比如a= a+b这样的算式,需要经过下面四个步骤才可以实现:

细看这几个步骤,有些疑问:

1、读a,那么a的值读出来后保存在CPU哪里?
2、读b,那么b的值都出来之后保存在哪里?

3、a+b的结果又保存在哪里?

这些问题都涉及到ARM处理器的内部,简单概括如下,我们先忽略各种CPU模式,用户模式等。 

CPU运行时,先去取指令,再执行指令

1)把内存a的值读入CPU寄存器R0

2)把内存b的值读入CPU寄存器R1

3)把R0和R1累计存入R0

4)把R0的值写入内存a

CPU内部寄存器分类

CPU内至少应该有数据缓冲寄存器,栈指针类寄存器、程序指针类寄存器、程序状态类寄存器及其他功能寄存器

1、数据缓冲寄存器

CPU内数量最多的寄存器是数据缓冲寄存器,名字用寄存器英文Register的首字母加数字组成,如R0、R1、R2等,不同的CPU其种类不同。

2、栈指针类寄存器

在计算机编程中有全局变量和局部变量的概念。从存储器的角度来看,对一个具有独立功能的完整程序来说,全局变量具有固定的地址,每次读写都是那个地址。而在一个子程序中开辟的局部变量则不同,用RAM中的哪个地址是不确定的,采用“后进先出”的原则使用一段RAM区域,这段区域被称为栈区。它有一个栈底的地址, 是一开始就确定的,当有数据进栈或者出栈时,地址就会连续变动,不然就放到同一个存储地址中了,CPU需要有个地方保存这个不断变化的地址,这就是栈指针(SP)寄存器。

3、程序指针类寄存器

计算机的程序存储在存储器中,CPU中有个寄存器指示将要执行的指令在存储器中的位置,这就是程序指针类寄存器。在许多CPU中,它的名字叫做程序计数器寄存器(PC),它负责告诉CPU将要执行的指令在存储器的什么地方。

4、程序运行状态类寄存器

CPU在进行计算过程中,会出现诸如进位、借位结果为0、溢出等情况,CPU内需要有个地方把他们保存下来,以便下一条指令结合这些情况进行处理,这类寄存器就是程序状态类寄存器,不同的CPU其名称不同,有的叫做标志寄存器,有的叫做程序状态字寄存器。

5、其他功能寄存器

不同的CPU中,除了具有数据缓冲,栈指针、程序指针、程序运行状态寄存器之外、还有表示浮点数运算、中断屏蔽等寄存器。

ARM Cortex-M中的寄存器

ARM Cortex-M处理器的寄存器主要有R0-R15及3个特殊功能寄存器,如上图所示,其中R0-R12为通用寄存器,R13为堆栈指针寄存器(SP)、R14是连接寄存器,R15为程序计数器(PC),特殊功能寄存器有预定义的功能,而且必须通过专用的指令来访问。

几条汇编指令

需要掌握的汇编指令并不多,只有几条。

  • 读内存指令:LDR,即Load之意
  • 写内存指令:STR,即Store之意
  • 加减指令:ADD与SUB
  • 跳转:BL,即Branch And Link 
  • 出栈指令:POP
  • 入栈指令:PUSH

汇编并不复杂:

加载/存储指令

加载指令LDR:LDR r0,[addrA]意思就是将地址addrA的内容加载到R0中

存储指令STR:  STR r0,[addrA]意思就是将r0的值存储到地址addrA上

加法运算指令ADD:ADD r0,r1,r2意思为:r0=r1+r2

减法运算指令SUB:SUB r0,r1,r2意思为:r0=r1-r2

寄存器入栈/出栈指令

函数运行的本质

如下是一个简单的程序,主函数里调用函数add_val():
 

void add_val(int *pa,int *pb){    volatile int tmp; tmp = *pa;    tmp = tmp + *pb;    *pa = tmp;}int main(void){    int a =1 ; int b = 2;    add_val(&a,&b);    return 0;}

 其中调用add_val函数的汇编代码如下: