完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
RT-Thread组成
内核基础 内核位于硬件层之上,包括内核库、实时内核实现 内核库是为了保证内核能够独立运行的一套小型的类似C库的函数实现子集,根据编译器的不同自带c库的情况也会有些不同(当使用GNU GCC编译器时,会携带更多的标准c库实现) C库又叫C运行库(C Runtime Library),提供了类似"strcpy"、“memcpy"等函数,有些也会包括"printf”、"scanf"等函数的实现,RTT Kernal Service Library仅提供内核用到的一小部分C库函数实现,这些函数前都会加上rt_前缀 实时内核实现包括:对象管理、线程管理 及 调度器、线程间通信管理、时钟管理及内存管理等 内核最小资源占用:3KB ROM 1.2KB RAM 线程调度 线程是RTT中的最小调度单位 线程调度算法:基于优先级的全抢占式多线程调度算法 支持256个线程优先级,针对stm32默认配置32个线程优先级,0优先级代表最高优先级 最低优先级留给空闲进程 支持创建多个相同优先级的线程,之间采用时间片的轮转调度算法进行调度,能使每个线程运行相应时间 线程数目只和硬件平台的具体内存相关 时钟管理 时钟节拍是RTT的最小时钟单位 RTT定时器提供两类定时器机制:
根据超时函数执行时所处的上下文环境,RTT的定时器可设置为HARD_TIMER模式或SOFT_TIMER模式 通常使用**定时器回调函数(超时函数)**完成定时任务 线程间同步 采用信号量、互斥量、事件集实现 见后续线程间同步介绍 线程间通信 支持邮箱、消息队列等通信机制,邮箱中一封邮件的长度固定为4字节大小;消息队列能接受不固定长度的消息并将其缓存在自己的内存空间中 邮箱效率比消息队列更高效 邮箱和消息队列的发送动作可安全用于中断服务例程中,线程可按优先级等待或按FIFO方式获取 见后续介绍 内存管理 RTT支持静态内存池管理和动态内存堆管理 静态内存池有可用内存时:系统对内存块分配的时间是恒定的 静态内存池 静态内存池空时:系统将申请内存块的线程挂起(线程等待一段时间后仍未获得内存块就放弃申请并返回)或阻塞(立刻返回),当其他线程释放内存块到内存池时,如果有挂起的待分配内存块的线程存在,则系统会将这个线程唤醒 动态内存堆 根据系统资源不同,可提供面向小内存的内存管理算法和面向大内存的SLAB内存管理算法 系统内包含多个地址不连续内存堆时,可使用memheap管理算法将多个内存堆”粘贴“在一起,”虚拟“出一个内存堆 IO设备管理 RTT将PIN、IIC、SPI、USB、USART等作为外设设备,统一通过设备注册完成 实现了可按名称访问的设备管理子系统,可按照统一的API界面访问硬件设备 根据MCU系统的特点对不同设备可挂接相应事件,当设备事件触发时,由驱动程序通知给上层的应用程序 见后续介绍 程序内存分配 一般MCU包括片上FLASH和片上RAM,RAM相当于内存,FLASH相当于硬盘,编译器会将一个程序分为多个部分,分别存储在MCU不同的存储区 keil编译完成后会显示 Program Size: Code=2932 RO-data=424 RW-data=28 ZI-data=1836 After Build - User command #1: fromelf --bin .buildrtthread-stm32.axf --output rtthread.bin Code:代码段,存放程序的代码部分 RO-data:只读数据段(Read Only)存放程序中定义的常量 RW_data:读写数据段(Read&Write)存放初始化为非0值的全局变量 ZI_data:零数据段(Zero)存放初始化为0的变量和未初始化的全局变量 编译以后工程会生成一个.map文件,说明各个函数占用的尺寸和地址 Total RO Size (Code + RO Data) 63528 ( 62.04kB) Total RW Size (RW Data + ZI Data) 22576 ( 22.05kB) Total ROM Size (Code + RO Data + RW Data) 63676 ( 62.18kB) RO Size包含Code和RO-data,表示程序占用FLASH大小 RW Size包括RW-data和ZI-data,表示程序运行时占用RAM大小 ROM Size包括Code、RO data、RW data,表示烧写程序所占用的FLASH大小 程序经过编译后生成的bin或hex文件称为可执行映像文件,包含RO段(包括Code、RO-data)和RW段(包括RW-data,ZI-data不包含在映像文件中),它们被存储在FLASH中。stm32上电后默认从FLASH启动,启动后会将RW段的RW-data搬运到RAM中,但不会搬运RO段,另外根据编译器给出的ZI地址和大小分配出ZI段,并将这块RAM区域清零。即CPU从FLASH读取执行代码,从RAM中读取所需的数据,根据预先规定的ZI地址分配清零的ZI段,剩余RAM空间作为动态内存堆 动态内存堆用于rt_malloc()申请内存 RTT自动初始化机制 自动初始化机制:初始化函数不需要被显式调用,只要在函数定义处通过宏定义的方式进行申明,就会在系统启动过程中被执行 自动初始化机制使用了自定义RTI符号段,将需要在启动时进行初始化的函数指针放到了该段中,形成一张初始化函数表,在系统启动过程中会遍历该表,并调用表中的函数,达到自动初始化的目的 自动初始化宏接口定义如下: [tr]初始化顺序宏接口描述[/tr]
由INIT_BOARD_EXPORT()申明的函数会被放在位于内存分布的RO段内的RTI符号段中,该RTI符号段中的所有函数在系统初始化时会被自动调用 内核对象模型 RTT内核采用面向对象的设计思想 系统及的基础设施都是一种内核对象 静态对象和动态对象 内核对象分成静态内核对象和动态内核对象
放在RW段和ZI段中,在系统启动后,在程序中初始化 在编译时决定使用堆栈空间等设置,占用RAM空间不依赖内存堆管理器,内存分配时间确定
位于内存堆,手工做初始化 在运行中动态调整堆栈设置等,依赖于内存堆管理器,运行时申请RAM空间,对象被删除后占用的RAM空间会被释放 内核对象管理架构 内核对象管理系统负责访问/管理所有内核对象,包括线程、信号量、互斥量、事件、邮箱、消息队列、定时器、内存池、设备驱动等 对象容器包含了每类对象的信息,包括对象类型、大小等。对象容器给每类内核对象分配一个链表,所有内核对象都被链接到该链表上 对于每一种具体内核对象和对象控制块,除了基本结构外,还有自己的扩展属性(私有属性),可认为每一种具体对象是抽象对象的派生,继承了基本对象的属性并在此基础上扩展了与自己相关的属性 对象管理模块中定义了通用的数据结构来保存各种对象的共同属性,具体对象只要在此基础上加上自己某些特有属性就可以表示自己的特征。 对象控制块 struct rt_object { char name[RT_NAME_MAX];//内核对象名称 rt_uint8_t type;//内核对象类型 rt_uint8_t flag;//内核对象参数 #ifdef RT_USING_MODULE void *module_id;//应用模块的id #endif rt_list_t list;//内核对象管理链表 }; typedef struct rt_object *rt_object_t;//将内核对象指针封装成“内核对象类” 如果是静态对象,对象类型的最高位为1,否则就是动态对象 系统最多能容纳的对象类别数目是127个 内核对象容器 struct rt_object_information { enum rt_object_class_type type;//对象类型 rt_list_t object_list;//对象链表 rt_size_t object_size;//对象大小 }; 一类对象由一个rt_object_information结构体管理,每个这类对象的具体实例都通过链表的形式挂接在object_list上。这一类对象的内存块尺寸由object_size标识出来 注意:每一类对象的具体实例占有的内存块大小都相同 使用接口rt_object_init()对未初始化的静态对象进行初始化 void rt_object_init(struct rt_object *object,//对象指针,不能为空指针或野指针 enum rt_object_class_type type,//对象类型,必须是rt_object_class_type中列出的 const char *name)//对象名,最大长度由RT_NAME_MAX决定,系统不关心它是否由' |