RT-Thread内核实现(1)

1. 编写main函数

一个工程如果没有main函数是编译不成功的,会出错。因为系统在开始执行的时候先执行启动文件里面的复位程序,复位程序里面会调用C库函数__main,__main的作用是初始化好系统变量,如全局变量,只读的,可读可写的等等。__main最后会调用__rtentry,再由__rtentry调用main函数,从而由汇编跳入到C的世界。这里面的main函数就是我们自己写的。

2. 软件仿真设置

为调试方便,选择软件仿真,如下图所示:
RT-Thread内核实现(1)_第1张图片

注. 需要更改时钟
时钟相关文件是system_ARMCM3.c开头,有一段代码:

/*----------------------------------------------------------------------------
  Define clocks
 *----------------------------------------------------------------------------*/
#define  XTAL            ( 5000000UL)      /* Oscillator frequency */

#define  SYSTEM_CLOCK    (5U * XTAL)

因此需要更改时钟如下图所示:
RT-Thread内核实现(1)_第2张图片

3. 线程栈

如果有全局变量,有子函数调用,又中断发生。那么系统在运行的时候,全局变量放在哪里,子函数调用时,局部变量放在哪里,中断发生时,函数返回地址放哪里,裸机系统中都放在一个叫栈的地方,栈是单片机RAM里面一段连续的内存空间,栈的大小一般在启动文件或者链接脚本里面指定,最后由__main函数进行初始化。
多线程系统中,每个线程是独立的,互不干扰的,所以要为每个线程分配独立的栈空间,这个栈空间通常是一个预先定义好的全局数组,也可以是动态分配的一段内存空间,它们都存在于RAM中。

4. 线程控制块

多线程系统中,线程的执行是由系统调度的。系统为了顺利的调度线程,为每个线程都额外定义了一个线程控制块,这个线程控制块就相当于线程的身份证,里面存有线程的所有信息,比如线程的栈指针,线程的名称,线程的形参等。有了这个线程控制块后,以后系统对线程的全部操作都可以通过这个线程控制块来实现。

线程的栈,线程的函数实体,线程的控制块最终需要联系起来才能由系统进行统一调度。那么这个联系的工作就由线程初始化函数rt_thread_init()来实现,该函数在thread.c文件中。

5.内存管理

通常存储空间可以分为两种:内部存储空间和外部存储空间。内部存储空间访问速度比较快,能够按照变量地址随机地访问,也就是我们通常所说的RAM(随机存储器),或电脑的内存;而外部存储空间内索堡村的内容相对来说比较固定,即使掉电后数据也不会丢失,可以把它理解为电脑的硬盘。

在RT-Thread中提供了多种内存分配算法,但是上层接口(API)却是统一的。这样做可以增加系统的灵活性;用户可以选择对自己更有利的内存管理策略,在不同的应用场合使用不同的内存分配策略。

RT-Thread中的内存管理包括内存的初始化、分配以及释放。

注:嵌入式系统中不能使用C语言的malloc和free.有以下几点原因:

  1. 这些函数在小型嵌入式系统中不总是可用,小型嵌入式设备中的RAM不足;
  2. 它们的实现可能非常的大,占据了相当大的一块代码空间;
  3. 它们几乎都不是线程安全的;
  4. 它们并不是确定的,每次调用这些函数执行的时间可能都不一样;
  5. 它们有可能产生碎片;
  6. 这两个函数会使得链接器配置得复杂;
  7. 如果允许堆空间的生长方向覆盖其他变量占据的内存,它们会成为debug的灾难。

RT-thread的内存管理模块的算法总体上可分为两类:静态内存管理与动态内存管理,而动态内存管理又根据可用内存的多少划分为两种情况:一种是针对小内存块的分配管理(小内存管理算法),另一种是针对大内存块的分配管理(SLAB管理算法),需要使用的时候开启其对应的宏定义即可。小堆内存管理模块主要针对系统资源比较少,一般用于小于2M内存空间的系统;而SLAB内存管理模块主要是在系统资源比较丰富时,提供了一种近似于多内存池管理算法的快速算法。两种内存管理模块在系统运行时只能选择其中之一或者完全不适用动态堆内存管理器,这两种内存管理模块提供的API接口完全相同。
警告:因为动态内存管理器需要满足多线程情况下的安全分配,会考虑多线程间的互斥问题,所以不要在中断服务例程中分配或者释放动态内存块。因为它可能会引起上下文被挂起等待。

你可能感兴趣的:(嵌入式)