RT_Thread内核源码分析(三)——线程

目录

1. 线程结构

2. 线程创建

2.1 静态线程创建

2.2 动态线程创建

2.3 源码分析

2.4 线程内存结构

3. 线程状态

3.1 线程状态分类

 3.2 就绪状态和运行态

 3.3 阻塞/挂起状态

3.3.1 阻塞工况

3.4 关闭状态

3.4.1 线程关闭接口

3.4.2 静态线程关闭

3.4.3 动态线程关闭

3.6 状态切换

4.线程调度

4.1 调度器相关变量

4.2 调度器初始化

4.3 调度器启动

4.4 调度切换

4.5 同优先级线程轮询

4.6 调度器逻辑图

5. 线程接口

5.1 设置钩子接口

5.2 线程新建

5.3 线程启动

5.4 线程阻塞接口

5.4.1 线程固定周期阻塞

5.4.2 线程固定延时阻塞

5.4.3 线程永久阻塞(挂起)

 5.5 线程强制恢复接口

5.6 线程定时器恢复

5.7 线程同优先级轮询

5.8 线程命令接口

5.9 线程关闭接口

5.9.1 静态线程关闭接口

5.9.2 动态线程关闭接口

5.10 线程退出接口

5.11 线程清除接口

5.12 线程查询

6. 系统线程

6.1 空闲线程

6.1.1 空闲线程作用

6.1.2 空闲线程创建和启动

6.1.3 空闲线程入口函数

6.2 软件定时器线程

6.2.1 定时器线程作用

6.2.2 定时器线程创建和启动

6.2.3 定时器入口函数


        本章讲解RT_Thread Nano实时操作系统的核心部分——线程。单核CPU裸核运行时,一般是多中断+单线程,即前后台系统;如实现多线程并发,须搭载操作系统。有的实时操作系统,使用的是任务并发,其实线程和任务在实时操作系统中本质是一样的。

本章基于RT_Thread Nano V3.1.5版本分析

1. 线程结构

        线程通过线程控制块(rt_thread)维护,rt_thread可分为几个部分:内核对象、线程链表节点、线程栈部分、线程状态部分、线程回调函数部分、线程计数器和定时器部分、线程事件部分、线程优先级部分等。声明如下:

struct rt_thread
{
    /*内核对象部分-STR*/
    char        name[RT_NAME_MAX];                    /**<线程内核对象名称 */
    rt_uint8_t  type;                                 /**<线程内核对象类型 */
    rt_uint8_t  flags;                                /**<线程内核对象标志 */
    rt_list_t     list;                               /**<线程内核对象链表节点 */

   /*线程部分-STR*/
    rt_list_t     tlist;                              /**< 线程链表节点 */
    void       *sp;                                   /**< 线程栈指针 */
    void       *entry;                                /**< 线程入口函数 */
    void       *parameter;                            /**< 线程入口函数的参数*/
    void       *stack_addr;                           /**< 线程栈首地址 */
    rt_uint32_t stack_size;                           /**< 线程栈大小 */
    rt_err_t    error;                                /**< 线程错误码 */
    rt_uint8_t  stat;                                 /**< 线程状态 */
    rt_uint8_t  current_priority;                     /**< 线程当前优先级 */
    rt_uint8_t  init_priority;                        /**< 线程初始化优先级 */
    /**< 线程优先级位域*/
    #if RT_THREAD_PRIORITY_MAX > 32 // 优先级个数大于32个时的位域 
    rt_uint8_t  number;
    rt_uint8_t  high_mask;
    #endif
    rt_uint32_t number_mask;
	/**< 线程事件*/
    #if defined(RT_USING_EVENT)
    rt_uint32_t event_set;
    rt_uint8_t  event_info;
   #endif
    /**< 线程执行周期*/
    rt_ubase_t  init_tick;                            /**< 线程执行周期*/
    rt_ubase_t  remaining_tick;                       /**< 线程剩余执行时间片数 */
    struct rt_timer thread_timer;                     /**< 线程定时器 */
    void (*cleanup)(struct rt_thread *tid);           /**< 线程清除函数回调 */
    rt_uint32_t user_data;                            /**< 线程私有数据*/
};
typedef struct rt_thread *rt_thread_t;

内核对象:线程结构继承了系统对象,即系统对象必须在线程控制块起始位置,内核对象在线程建立后会插入到内核对象容器中,可参考《RT_Thread内核源码分析(二)——链表和对象管理》。

线程栈:*sp、*stack_addr、stack_size三个元素用于管理线程栈,每个线程都会分配一块内存,用于存取线程的局部变量和线程切换时的运行环境,各个线程栈相互独立。*sp指向栈当前使用位置(栈顶);*stack_addr指向栈首地址,stack_size表示线程栈空间大小。

        线程栈有2种访问方式:低地址->高地址、高地址->低地址;具体使用哪一种需要与CPU内核设置保持一致。操作系统通过宏定义ARCH_CPU_STACK_GROWS_UPWARD进行设置。

线程回调函数:*entry指向线程回调函数,*parameter为回调函数参数指针。

优先级部分:init_priority表示线程新建时分配的优先级,数值越小表示优先级越高。current_priority表示实际运行时的优先级,大部分情况current_priority等于init_priority,当出现优先级反转现象时,优先级继承机制会运行,current_priority会被改写。比如:高优先级线程H等待低优先级线程L释放资源而被阻塞,线程H出现优先级翻转现象,优先级继承机制将线程L优先级current_priority修改为与线程H一样,当线程H阻塞解除时,线程L优先级current_priority再修改为init_priority。

        线程调度为了快速检测优先级,通过标志字rt_thread_ready_priority_group的bit位来判断就绪线程优先级的分布,如下所示:

        将就绪线程优先级的位域合并到一起,即可得出总的优先级分布矩阵,当总优先级个数小于32时,仅需要32位无符号整数即可表示所有优先级状态,即线程参数number_mask,比如:优先级为10,则number_mask=10000000000B。

        当总优先级个数大于32时(最大不超过256),使用number、high_mask表示,number表示优先级在数组rt_thread_ready_table中的位置,high_mask表示优先级掩码。比如:优先级为60,则number=60/8=7;因60%8=4,则high_mask=10000B;如果线程切换为就绪状态,则将该线程优先级合并到系统总优先级位域,即rt_thread_ready_table[number]|=high_mask

同级线程轮询参数:       

        如果最高优先级线程有多个,这几个相同优先级的线程会轮询执行,线程轮询切换在 SysTick中断中进行,为防止频繁切换,每个线程通过参数init_tick设置轮询保持时间,通过参数remaining_tick监视执行时间;比如:线程A(pThreadA)、线程B(pThreadB)均为最高优先级线程,当线程A执行时间超过pThreadA->init_tick个时间片后,才会由切换为线程B运行。

        具体代码分析参照章节5.7。

 线程定时器部分

        RT_Thread操作系统是通过软件定时器thread_timer进行线程延时阻塞和恢复,定时器回调函数固定为rt_thread_timeout;其功能为恢复线程(阻塞态->就绪态),具体代码分析参照章节5.6。

2. 线程创建

         线程创建有两种接口:动态线程创建、静态线程创建;

        rt_thread_init:静态线程新建,线程栈和线程控制块均为全局变量,内存无法回收利用,但是方便维护与监视。

       rt_thread_create:动态线程新建,线程栈和线程控制块均从系统栈区动态申请,内存可以回收利用。

        注:内存申请和释放涉及的内存管理模块,本章暂不详细说明,后文的分析均依据动态任务创建的方式分析!

2.1 静态线程创建

        通过静态接口创建线程,需先定义好线程控制器和线程栈,示例如下:

 /*****新建静态线程接口******/
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_thread g_ThreadA;
unsigned char cStack[0x400];
int iThreadApara;
void vThreadA(*int para)
{
  while(1);
}
void main()
{
    // 新建静态线程threadA,回调函数vThreadA,线程栈大小为16K,优先级为10,同优先级线程轮询保持时间为10个时间片
    rt_thread_init(&g_ThreadA,"threadA",vThreadA,&iThreadApara,cStack,0x4000,10,10);
}

2.2 动态线程创建

        动态创建线程,无需定义线程控制器和线程栈,二者均通过申请动态内存存储。

 /*新建动态线程接口*/
rt_thread_t rt_thread_create(const char *name,              // 线程名称
                             void (*entry)(void *parameter),// 线程回调函数指针
                             void       *parameter,         // 线程回调函数的参数指针
                             rt_uint32_t stack_size,        // 线程栈大小
                             rt_uint8_t  priority,          // 线程优先级
                             rt_uint32_t tick)              // 线程同优先级轮询保持时间

 /*****新建动态线程举例*****/

int iThreadApara;
void vThreadA(*int para)
{
  while(1);
}
void main()
{
    // 新建动态线程threadA,回调函数vThreadA,线程栈大小为16K,优先级为10,同优先级线程轮询保持时间为10个时间片
    rt_thread_create("threadA",vThreadA,&iThreadApara,0x4000,10,10);
}

2.3 源码分析

        静态线程创建,先对线程的系统对象进行初始化,并挂接到线程对象容器,然后进行线程数据初始化,不进行线程链表挂接,所以新建线程状态为初始化(RT_THREAD_INIT)。

 /*新建静态线程*/
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_ASSERT(thread != RT_NULL);
    RT_ASSERT(stack_start != RT_NULL);

    /*内核对象初始化--初始化线程对象,并挂接到线程对象容器 */
    rt_object_init((rt_object_t)thread, RT_Object_Class_Thread, name);
	
    /*线程数据初始化 */
    return _rt_thread_init(thread,name,entry,parameter,stack_start,stack_size,         
                          priority,tick);
}

         动态线程创建,先申请线程控制器内存,然后初始化系统对象,并挂接到线程对象容器,再进行线程栈内存申请,最后进行线程数据初始化,不进行线程链表挂接,所以新建线程状态为初始化(RT_THREAD_INIT)。

/*新建动态线程*/
rt_thread_t rt_thread_create(const char *name,              // 线程名称
                             void (*entry)(void *parameter),// 线程回调函数指针
                             void       *parameter,         // 线程回调函数的参数指针
                             rt_uint32_t stack_size,        // 线程栈大小
                             rt_uint8_t  priority,          // 线程优先级
                             rt_uint32_t tick)              // 线程同优先级轮询保持时间

{
    struct rt_thread *thread;
    void *stack_start;
    /*动态分配线程控制器内存,并初始化线程对象,挂接至线程容器链表*/
    thread = (struct rt_thread *)rt_object_allocate(RT_Object_Class_Thread, name);
    if (thread == RT_NULL) return RT_NULL;
     /*动态分配线程栈内存*/
    stack_start = (void *)RT_KERNEL_MALLOC(stack_size);
    if (stack_start == RT_NULL)
    {
        /* 分配栈失败,释放线程对象*/
        rt_object_delete((rt_object_t)thread);
        return RT_NULL;
    }
}

         静态创建和动态创建线程均通过调用函数_rt_thread_init进行线程数据初始化,代码分析如下:

/* 线程数据初始化*/
static 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)        // 线程同优先级轮询保持时间
{
    /*1. 线程链表节点初始化*/
    rt_list_init(&(thread->tlist));

    /*2. 线程回调函数和参数设置*/
    thread->entry = (void *)entry;
    thread->parameter = parameter;

    /*3. 线程栈初始化*/
    thread->stack_addr = stack_start;// 首地址
    thread->stack_size = stack_size; // 栈大小

     /*3.1 栈内容初始化为#号 */
    rt_memset(thread->stack_addr, '#', thread->stack_size);
     /*3.2 初始化栈指针SP*/
    #ifdef ARCH_CPU_STACK_GROWS_UPWARD
    /*3.3 栈访问方式:低地址->高地址,低地址为栈底*/
    thread->sp=(void*)rt_hw_stack_init(thread->entry, thread->parameter,
                         (void*)((char*)thread->stack_addr),(void*)rt_thread_exit);
    #else
    /*3.4 栈访问方式:高地址->底地址,高地址为栈底*/
    thread->sp=(void*)rt_hw_stack_init(thread->entry, thread->parameter,
                           (rt_uint8_t*)((char*)thread->stack_addr+thread->stack_size - 
                           sizeof(rt_ubase_t)),(void*)rt_thread_exit);
    #endif

    /*4. 优先级初始化*/
    RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX);
    thread->init_priority    = priority;
    thread->current_priority = priority;
    /*4.1 优先级位域初始化*/
    thread->number_mask = 0;
#if RT_THREAD_PRIORITY_MAX > 32
    thread->number = 0;
    thread->high_mask = 0;
#endif

    /*5. 同优先级线程轮询计数初始化*/
    thread->init_tick      = tick;
    thread->remaining_tick = tick;
    /*6. 线程状态初始化 */
    thread->error = RT_EOK;
    thread->stat  = RT_THREAD_INIT;
    /*7. 初始化清除函数和用户数据*/
    thread->cleanup   = 0;
    thread->user_data = 0;
    /*8. 线程定时器初始化*/
    rt_timer_init(&(thread->thread_timer), // 定时器控制器
                  thread->name,            // 定时器名称,取线程名称
                  rt_thread_timeout,       // 定时器回调函数,固定为rt_thread_timeout
                  thread,                  // 定时器回调函数参数,取线程指针
                  0,                       // 定时时间
                  RT_TIMER_FLAG_ONE_SHOT); // 定时器类型:单次定时器
	/*9

你可能感兴趣的:(RT_Thread内核源码分析(三)——线程)