前面第6章实现的线程的定义与线程切换的实现没有支持多优先级,只支持两个线程的互相切换,下面给大家介绍实现线程优先级的功能,在RT-Thread中,数字优先级越小,逻辑优先级越高。RT-Thread是一个根据优先级来调度的抢占式实时操作系统,即每个系统周期到来时,调度器都会扫面就绪列表,选取优先级最高的线程去执行。
在介绍实现优先级之前,先给大家介绍几个概念,第一个是临界段的保护,临界段用一句话概括就是一段在执行的时候不能被中断的代码段。在RT-Thread里面,这个临界段最常出现的就是对全局变量的操作,全局变量就好像是一个枪靶子,谁都可以对他开枪,但是我开枪的时候,你就不能开枪,否则就不知道是谁中了靶子,为了保护临界段,我们要实现关中断函数,开中断函数。如临界段代码的应用在进入临界段之前,我们会把中断关闭,退出临界段时再把中断打开。第二个是对象容器的实现。在RT-Thread中,所有的数据结构都称为对象。如:线程,信号量、互斥量、事件、邮箱、消息队列、内存堆、内存池、设备和定时器等。在RT-Thread中,为了方便管理这些对象,专门定义了一个对象类型数据结构,代表了数据类型。在RT-Thread中,每个对象都会有对应的一个结构体,这个结构体叫做该对象的控制块。如线程会有一个线程控制块。在控制块的开头放置的是对象结构体的成员,具体的见书里面。什么是容器,在RT-Thread中,每当用户创建一个对象,如线程,就会将这个对象放在一个叫做容器的地方,这样做的目的是为了方便管理,通过扫描容器的内核对象来获取各个内核对象的状态,然后输出调试信息。容器的接口实现方式:容器在定义的时候,大小被固定了,但容器里面的成员是否初始化就不一定了,从容器里获取指定类型的对象函数会遍历整个容器对象,如果对象的类型等于我们指定的类型,那么就返回该容器成员的地址。每创建一个对象,都需要先将其初始化,主要分成两个部分的工作,首先将对象控制块里面与对象相关的成员初始化,然后将该对象插入到对象容器中,对象初始化函数在线程初始化函数里面被调用。第三个比较重要的概念是空闲线程与阻塞延时的实现。当线程需要延时,进入阻塞状态,需要cpu执行空闲线程。
如在下一个系统周期来临时,调度器需要选取优先级最高的线程去执行,在程序里我们用一个32位的数表示线程的优先级。我们规定在这个32位的数中,位数越低,优先级越高,如第一个置1的位是位1,即表示此时就绪的线程当中,优先级最高的是线程1,然后调度器从线程优先级表的索引1下取出线程的线程控制块,从而切换到线程1。但是,单片机没有眼睛,并不能跟人一样一眼就从线程就绪优先级组中看到那个第一个置1的位,怎么办?专门写了一个函数,通过查表的方法获得最高优先级的线程。我们通过线程优先级组获得最高的优先级线程。然后根据线程优先级表即就绪列表,我们可以理解为就绪列表就是线程优先级表。线程优先级表是全局数组。线程优先级的数据类型是rt_list,每个索引号对应线程的优先级,该索引下维护着一条双向链表,当线程就绪时,线程就会根据优先级插入到对应索引的链表,同一个优先级的线程都会被插入到同一条链表中。最后我们将线程插入到线程优先级表和移除分别用rt_schedule_insert_thread()和rt_schedule_remove_thread()这两个函数实现。通过就绪优先级组和线程优先级表就可以实现多优先级的线程了。然后根据优先级来决定第一个运行的线程。而系统调度函数也通过线程的优先级进行切换。根据优先级切换的原理如下:首先获取就绪的最高优先级,然后获取就绪的最高优先级对应的线程控制块,如果目标线程不是当前线程就要进行线程切换。修改阻塞延时函数rt_thread_delay()的实现原理:将线程的状态改为挂起,接下来将进入延时,暂时放弃CPU的使用权。然后根据优先级将线程就绪优先级组中对应的位清零。还有一些函数才能最终实现线程的优先级,今天实在太饿了,没有精力完成了。
此内容由EEWORLD论坛网友沈婷婷原创,如需转载或用于商业用途需征得作者同意并注明出处