学习目标:
学习内容:rt-thread的软件结构
1.组件component:shell command 命令行指令
Device FS 设备虚拟文件系统
POSIX 可移植操作系统接口
Lwlp: tcp / ip栈
gui 图形界面
2.Kernel 内核: kservice.c object.c scheduler.c thread.c ipc.c mem.c mempool.c device.c module.c
3.Porting 移植:
4.Hardware and Device : 硬件和外设
2.线程
线程的存在让系统具有可以并行处理问题的能力,根据优先级顺序完成了系统的分层。
在线程调度器中,中断处理函数,调度器的上锁部分和禁止中断的代码是不可抢占的。
为了顺利完成优先级的调度,采用了队列和链表的数据结构形式来实现调度任务。
关于所谓的基于位图的优先级算法可以降低复杂度的说法(未解决)
所谓的线程控制块:
线程控制块是操作系统控制线程的一种数据结构,他会存放线程的名称,优先级,线程与线程之间连接用的链表结构,线程的等待事件等。
在rt_thread中,线程控制块由struct rt_thread表示。另一种表示方式为rt_thread_t 表示的是线程的句柄(handle),二者的关系为 typedef struct rt_thread* rt_thread_t
所谓的线程状态:
即使是多任务系统,一个时间内也只能允许一个线程在处理器中运行,在RT_thread的操作系统中,一个线程主要包含5种运行状态:
1.Rt_Thread_init 线程初始状态,当线程刚开使创建时就处于这个状态,这个状态下的线程不参与系统的任务调度。
2.Rt_thread_suspend 挂起,阻塞态。线程此时被挂起:他可能因为此时的资源不可用而挂起等待,也可能因线程主动延时一段时间而被挂起。这个状态下,线程不参与调度。
3.Rt_thread_ready 就绪状态。线程正在运行;或者当前线程运行完让出处理器后,操作系统寻找更高级的就绪态线程运行。
4.rt_thread_running 运行态。系统当前正在运行,在单核的系统中,只有rt_thread_self()函数返回的线程处于这个状态,在多核系统中则无此限制。
5.rt_thread_close 线程的结束态,当线程运行结束时将处于这个状态,线程不参与线程的调度。
线程的整个生命周期中,就是以上几种状态相互切换的过程。
空闲线程:
rt-thread在操作系统中提供了空闲线程,空闲线程具有最低的优先级,当系统中无线程需要运行时才会执行空闲线程。
所谓的钩子函数:用户提供的一段代码,在系统运行的某一路径上设置一个钩子,当系统经过这个位置时,转而执行钩子函数,
然后再返回到它的正常路径上。
空闲线程和钩子函数可以搭配使用,比如系统空闲的时候可以执行一些特定的任务,比如系统运行指示灯闪烁,电源管理等,真正的线程删除函数也是放在了空闲线程中(这话待理解)
调度器相关接口:
调度器初始化:
初始化调度器需要使用的全局变量
void rt_system_scheduler(void);
启动调度器:
void rt_system_scheduler_start(void);
执行调度:
void rt_scheduler(void);
设置调度器钩子:
void rt_scheduler_sethook(void (*hook)(struct rt_thread* from, struct rt_thread* to));
hook函数的声明:
void hook(struct rt_thread* from,struct rt_thread* to);
from 表示系统所需要切换出的线程控制块指针。
to 表示系统所要切换到的线程控制块指针
在编写自己的钩子函数时,稍有不慎就会影响到整个系统,基本不允许调用系统的API,更不应该导致当前运行的上下文挂起。
线程的相关接口:
线程的创建
rt_thread_t rt_thread_creat(const char* name,
void(*entry)(void* parameter), void* parameter,
rt_uint32_t stack_size,
rt_uint8_t priority, rt_uint32_t tick); //线程控制块句柄的建立
线程的删除:
rt_err_t rt_thread_delete(rt_thread_t thread);
线程的初始化:初始化的是静态线程
rt_err_t rt_thread_init(struct rt_thread* thread,
const char* name,
void(*entry)(void* parameter),void* parameter,
void* stack_start,rt_uint32_t stack_size,
rt_uint8_t priority,rt_uint32_t tick
)
线程脱离函数:
rt_err_t rt_thread_detach (rt_thread_t thread);
注意:rt_thread_delete()函数对应的是rt_thread_create()创建的动态线程,而rt_thread_detach()对应的是rt_thread_init()创建的静态线程。
线程启动函数:
rt_err_t rt_thread_startup(rt_thread_t thread);
通过线程启动函数可以将线程的状态改编为就绪状态,并放到相应的优先级队列中等待调度,如果新启动的线程优先级比当前的优先级高,则马上切换。
当前的线程:
获得当前正在执行的线程句柄:
rt_thread_t rt_thread_self(void);
如果调度器还有启动,则返回RT_NULL。不要在中断服务函数中调用该函数,因为该函数并不能准确的反应当前执行的线程。
线程让出处理器:
rt_err_t rt_thread_yield(void)
线程主动要求让出处理器资源,调度器选择相同优先级下的下一个线程进行执行,但其仍然在就绪队列中。
线程动作:从就绪优先级列表中删除,挂载到优先级队列链表尾部,激活调度器进行上下文的切换。
线程的睡眠:
就是当前线程的延时函数:
rt_err_t thread_sleep(rt_tick_t tick)
rt_err_t thread_dealy(rt_tick_t tick)
l两个函数的作用相同,都可以是当前的线程挂起指定的时间,该时间过后线程会再次被唤醒进入就绪状态。
线程挂起函数:
当进行某些系统API的调用时,(比如 rt__delay,rt_sem_take,rt_mb_recv)将会主动激活线程挂起函数:
rt_err_t rt_thread_suspend(rt_thread_t thread);
通常不使用rt_thread_suspend来挂起线程本身,如果非要用,需要在挂起后立即调用rt-shedule()函数来触发上下文的切换
线程的恢复:
rt_err_t thread_resume(rt_thread_t thread)
线程的控制:
当需要对线程进行如修改优先级等命令时:
rt_err_t rt_thread_control(rt_thread_t thread,rt_uint8_t cmd,void *arg)
初始化空闲线程:
系统在运行工程中必须存在一个最终可以运行的程序,调用空闲线程初始化函数:
void rt_thread_idle_init(void);
空闲线程运行时自动调用空闲钩子函数:
void rt_thread_idle_setkook(void(*hook)(void));
空闲钩子函数位于优先级最低的空闲线程中,因而该钩子函数不得引起线程挂起操作。
(空闲线程应该永远为就绪状态,所以不应该被挂起)
在rt_thread的使用过程中,经常提到关于上下文的概念,那么其上下文主要包括:
1.中断服务历程(其工作在非线程的执行环境下,为一种特权模式,中断服务是一种高于任何线程的存在)
2.普通线程
3.空闲线程