RT-Thread常见问题

1. RT-Thread的线程栈最大能够设置成多大?

  • RT-Thread的线程栈最大仅支持64k - 4(因为线程块中的字段是rt_uint16_t类型,取整后最大是64k - 4字节)

2. rt_kprintf为什么是轮询方式,并且允许抢占?

  • rt_kprintf被设计为RT-Thread系统中的简单、直观log输出,包括能够在中断服务例程中使用。所以,这就要求:1. rt_kprintf不应该导致当前上下文被堵塞(中断服务不能被堵塞);2. 输出log后不应该再行产生中断(不应该使用中断方式的DMA);3. rt_kprintf运行过程中允许被中断,不抢占。综合以上因素,rt_kprintf必须是采用轮询、即时方式输出。如果有其他需求,用户应另行实现输出函数(保持输出数据一致性,输出信息对系统时序无干扰等)。

3. 创建了一个线程,然后在finsh中用list_thread(),结果这个线程的error总是-2,也就是-RT_ETIMEOUT,线程是个while循环,执行一段程序,然后rt_thread_delay(2)。程序运行没发现问题,可是 list_thread()总是显示错误呢?

  • 因为程序在执行rt_thread_delay/rt_thread_sleep函数时,当指定的时间超时结束时,系统会把这个线程给唤醒,然后统一对这类线程赋了一个-RT_ETIMEOUT的错误号。唤醒的线程继续执行后续的rt_thread_delay/rt_thread_sleep函数,在末尾的时候会把线程本身的错误号清成0。
  • 所以,有的时候会发现错误号是-RT_ETIMEOUT,但它并不一定代表是错误。

4. 程序开始运行时串口输出: (thread→stat == RT_THREAD_INIT) assert failed at rt_thread_startup:200。这句是什么原因造成的?200代表什么?

  • rt_thread_startup是函数名;200是文件中的行号,即thread.c中的第200行。
  • 当出现了这类assert问题时,可以在相应的函数、相应的行号中查找是因为什么导致代码不能执行通过,一般都是OS的代码检测到错误而停下来了。

5. 线程退出或删除,它真正是在哪里删除的?

  • 线程自动运行结束退出后,线程状态会变成init状态不再参与调度,当系统空闲调度到idle线程时,idle线程会做真正的删除动作,包括线程的TCB和栈。线程删除的过程也类似,先更改为init状态,然后由idle线程做真正的删除动作。

6. 什么是动态线程?什么是静态线程?两者有什么区别?

  • RT-Thread中支持静态和动态两种定义方式。用线程来举例的话,rt_thread_init对应静态定义方式,rt_thread_create对应动态定义方式。
  • 使用静态定义方式时,必须先定义静态的线程控制块,并且定义好堆栈空间,然后调用rt_thread_init来完成线程的初始化工作。采用这种方式,线程控制块和堆栈占用的内存会放在RW段,这段空间在编译时就已经确定,它不是可以动态分配的,所以不能被释放,而只能使用 rt_thread_detach函数将该线程控制块从对象管理器中脱离。
  • 使用动态定义方式rt_thread_create时,RT-Thread会动态申请线程控制块和堆栈空间。在编译时,编译器是不会感知到这段空间的,只有在程序运行时,RT-Thread才会从系统堆中申请分配这段内存空间。当不需要使用该线程时,调用rt_thread_delete函数就会将这段申请的内存空间重新释放到内存堆中(如果线程执行完毕,退出时,系统也会自动回收线程控制块和堆栈空间)
  • 这两种方式各有利弊,静态定义方式会占用RW/ZI空间,但是不需要动态分配内存,运行时效率较高,实时性较好。 动态方式不会占用额外的RW/ZI空间,占用空间小,但是运行时需要动态分配内存,效率没有静态方式高。
  • 总的来说,这两种方式就是空间和时间效率的平衡,可以根据实际环境需求选择采用具体的分配方式。

7. 系统中断什么时候打开?

  • 系统刚启动时会进行系统的初始化,这个时候不应该打开系统的中断(试想,如果打开了中断,这个时候中断触发了,哪个中断服务例程来进行中断处理?)
  • 系统通常在rt_hw_board_init中配置时钟中断,这个时候可以打开时钟中断,但是系统的中断依然未被打开;
  • 每个任务的初始上下文都是由rt_hw_stack_init函数进行构造最初的上下文环境,每个任务的上下文环境默认都是中断打开状态;当系统调度到第一个任务时,系统的中断将被自动打开。

8. 系统时钟溢出后会怎么样?

  • 系统的最大ticks由RT_TICK_MAX定义,默认为0xFFFFFFFF,溢出后会自动回绕归零。如果tick精度是10ms、5ms或有1ms,那么真正回绕的时间将是(小数部分略掉,近似值):497天,248天,49天

9. 为什么定时器里判断超时的条件是((current_tick - t→timeout_tick) < RT_TICK_MAX/2)?

  • 因为系统时钟溢出后会自动回绕。取定时器比较最大值是定时器最大值的一半,即RT_TICK_MAX/2(在比较两个定时器值时,值是32位无符号数,相减运算将会自动回绕)。系统支持的定时器最大长度就是RT_TICK_MAX的一半:即248天(10ms/tick),124天(5ms/tick),24.5天(1ms/tick)

10. 信号量,邮箱,互斥量,消息队列是否可以在中断中使用?

  • 互斥量只能在线程中使用,因为有 owner。信号量,邮箱,消息队列都可以在中断中进行发送操作,不可以进行接收操作。

11. 运行RT-Thread是否需要MMU,能否使用MMU?

  • 当前运行RT-Thread不需要使用MMU,它基本上完全运行于内核模式,并没有用户模式的区分。RT-Thread在ARM9上能够使用MMU,它会把内存地址空间做1:1的映射,并把异常向量映射到0地址。

12. 如何理解RT-Thread中的环形缓冲区(Buffer Ring)?

  • 其实编程指南里面已经描述了,如果仍不能理解,可以参考以下示意图:

  • 环形Buffer的特点:
  • 通常包含一个读指针(read_index)和一个写指针(write_index)。读指针指向环形Buffer中第一个可读的数据,写指针指向环形Buffer中第一个可写的缓冲区。通过移动读指针和写指针就可以实现Buffer的数据读取和写入。在通常情况下,环形Buffer的读用户仅仅会影响读指针,而写用户也仅仅会影响写指针。
  • 环形Buffer的原理:
  • 首先在内存里开辟一片区域(大小为 buffer_size),对于写用户,顺次往Buffer里写入东西,一直写到最后那个内存(buffer_size)时再将写指针指向内存区域的首地址,即接下来的数据转个环放到最开始处,只有遇到Buffer里的有效存储空间为0时,才丢掉数据;对于读用户,顺次从Buffer里读出东西,一直读到最后那个内存(buffer_size)时再将读指针指向内存区域的首地址,即接下来转个环从最开始处取数据。
  • 有效存储空间与buffer_size的区别:有效存储空间是指那些没有存放数据,或者以前存放过但已经处理过的数据,就是可用的空间大小;而buffer_size指的是总大小。
  • 通过上面介绍可知,环形Buffer仍然是一长条区域,只不过其空间会被循环使用而已。示意图如下,根据读写指针的位置可分为两种情况,其中阴影填充部分为数据/已用空间,空白的为可用空间,即有效存储空间。 

你可能感兴趣的:(rt-thread)