4.RT-Thread中的线程?
/**
* 线程结构
*/
struct rt_thread {
/** Object对象 */
char name[RT_NAME_MAX]; /**< 线程的名字 */
rt_uint8_t type; /**< 对象的类型 */
rt_uint8_t flags; /**< 线程的标志 */
#ifdef RT_USING_MODULE
void *module_id; /**< 应用模块的ID */
#endif
rt_list_t list; /**< 对象链表 */
rt_list_t tlist; /**< 线程链表 */
void *sp; /**< 栈顶指针 */
void *entry; /**< 入口函数 */
void *parameter; /**< 附加参数 */
void *stack_addr; /**< 栈底地址 */
rt_uint16_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
rt_uint8_t number; /**< 线程优先级对应的组号: current_priority >> 3 */
rt_uint8_t high_mask; /**< 线程位号掩码: (1 << 位号) 位号: (current_priority & 7) */
#endif
rt_uint32_t number_mask; /**< 组号掩码: (1 << 组号) */
#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; /**< 私有用户数据 */
};
总的来看,线程皆有由几类成员组成:object,栈相关信息,优先级信息,事件,定时器信息,私有数据指针。在RT-Thread中提供的线程接口函数都是围绕线程的各种状态展开的。
/*
* 线程的状态定义
*/
#define RT_THREAD_INIT 0x00 /**< 初始化状态 */
#define RT_THREAD_READY 0x01 /**< 就绪状态 */
#define RT_THREAD_SUSPEND 0x02 /**< 挂起状态 */
#define RT_THREAD_RUNNING 0x03 /**< 运行状态 */
#define RT_THREAD_BLOCK RT_THREAD_SUSPEND /**< 阻塞状态 */
#define RT_THREAD_CLOSE 0x04 /**< 关闭/结束状态 */
处于初始化状态的接口函数有:rt_thread_init,rt_thread_create
这两个函数的区别在于一个是静态的初始化一个线程实体,另一个是动态的创建线程实体再来初始化。
/*******************************************************************************************
** 函数名称: rt_thread_init
** 函数功能: 静态初始化线程实例
** 入口参数: thread 线程对象句柄
** name 线程的名字
** entry 线程的入口函数
** parameter 附加参数
** stack_start 栈底指针
** stack_size 栈的大小
** priority 线程的优先级
** tick 线程的初始滴答数(能运行的时间片值)
** 返 回 值: 成功返回RT_EOK
** 调 用: 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_create
** 函数功能: 动态的创建线程
** 入口参数: name 线程的名字
** entry 线程的入口
** parameter 附加参数
** stack_size 线程栈的大小
** priority 线程的优先级
** tick 线程的初始化滴答数
** 返 回 值: 线程对象句柄
** 调 用:
*******************************************************************************************/
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(thread,
name,
entry,
parameter,
stack_start,
stack_size,
priority,
tick);
return thread;
}
最终都调用到_rt_thread_init函数:
/*******************************************************************************************
** 函数名称: _rt_thread_init
** 函数功能: 初始化线程实例
** 入口参数: thread 线程对象句柄
** name 线程的名字
** entry 线程的入口函数
** parameter 附加参数
** stack_start 栈底指针
** stack_size 栈的大小
** priority 线程的优先级
** tick 线程的初始滴答数(能运行的时间片值)
** 返 回 值: 成功返回RT_EOK
** 调 用: 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)
{
/** 初始化线程链表节点成员 */
rt_list_init(&(thread->tlist));
thread->entry = (void *)entry;
thread->parameter = parameter;
thread->stack_addr = stack_start;
thread->stack_size = (rt_uint16_t)stack_size;
/** 初始化线程的栈 */
rt_memset(thread->stack_addr, '#', thread->stack_size);
thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter,
(void *)((char *)thread->stack_addr + thread->stack_size - 4),
(void *)rt_thread_exit);
RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX);
thread->init_priority = priority;
thread->current_priority = priority;
thread->init_tick = tick;
thread->remaining_tick = tick;
thread->error = RT_EOK;
/** 创建线程时,线程处于INIT状态 */
thread->stat = RT_THREAD_INIT;
thread->cleanup = 0;
thread->user_data = 0;
/** 初始化线程内嵌的定时器 */
rt_timer_init(&(thread->thread_timer),
thread->name,
rt_thread_timeout,
thread,
0,
RT_TIMER_FLAG_ONE_SHOT);
return RT_EOK;
}
在这个函数中有几点需要注意:
1.线程的栈是如何初始化的?
线程的栈是这样布局的:
-----------
| Entry | <--- 线程的入口函数
-----------
| LR | <--- 返回地址
-----------
| R12 | <--- 通用寄存器
-----------
| R11 |
-----------
| R10 |
-----------
| R9 |
-----------
| R8 |
-----------
| R7 |
-----------
| R6 |
-----------
| R5 |
-----------
| R4 |
-----------
| R3 |
-----------
| R2 |
-----------
| R1 |
-----------
| R0 |
-----------
| CPSR | <--- 当前程序状态寄存器
-----------
| SPSR | <--- 保存程序状态寄存器
-----------
2.线程内嵌的定时器的目的?
线程内嵌定时器来实现线程状态的改变,比如当需要让线程休眠多少个时钟滴答时,可以将线程设置为挂起状态,然后设置定时器的超时时间启动定时器,这样当定时器超市就能唤醒线程
3.此时线程是否具备了运行条件?
初始化后线程的状态为INIT状态,并不能运行,要转换成就绪状态需调用rt_thread_startup
设置线程为就绪状态的接口函数有:rt_thread_control,rt_thread_resume,rt_thread_timeout
rt_thread_control
rt_schedule_insert_thread
thread->stat = RT_THREAD_READY
rt_thread_resume
rt_schedule_insert_thread
thread->stat = RT_THREAD_READY
rt_thread_timeout
rt_schedule_insert_thread
thread->stat = RT_THREAD_READY
可以发现设置线程为就绪状态的接口函数实际为rt_schedule_insert_thread
/*********************************************************************************************************
** 函数名称: rt_schedule_insert_thread
** 函数功能: 将线程插入到就绪队列中
** 入口参数: thread 线程对象句柄
** 返 回 值: 无
** 调 用:
*********************************************************************************************************/
void rt_schedule_insert_thread(struct rt_thread* thread)
{
register rt_base_t temp;
/** 参数检查 */
RT_ASSERT(thread != RT_NULL);
/** 禁止全局中断 */
temp = rt_hw_interrupt_disable();
/** 设置线程的状态为就绪态 */
thread->stat = RT_THREAD_READY;
/** 将线程插入到优先级队列中 */
rt_list_insert_before(&(rt_thread_priority_table[thread->current_priority]),
&(thread->tlist));
#if RT_THREAD_PRIORITY_MAX <= 32
RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("insert thread[%.*s], the priority: %d\n",
RT_NAME_MAX, thread->name, thread->current_priority));
#else
RT_DEBUG_LOG(RT_DEBUG_SCHEDULER,
("insert thread[%.*s], the priority: %d 0x%x %d\n",
RT_NAME_MAX,
thread->name,
thread->number,
thread->number_mask,
thread->high_mask));
#endif
/** 设置就绪表和就绪组 */
#if RT_THREAD_PRIORITY_MAX > 32
rt_thread_ready_table[thread->number] |= thread->high_mask;
#endif
rt_thread_ready_priority_group |= thread->number_mask;
/** 使能全局中断 */
rt_hw_interrupt_enable(temp);
}
在函数中除了将线程设置为就绪状态外,还有几个动作需要关注:
1.将线程加入线程优先级链表中,在RT-Thread支持的优先级个数可以为32或256,但系统所支持的线程数目并没有限制。因此,处于同一优先级的线程是可以多个的,这些线程将以链表连接起来,这些具有相同优先级的线程的调度方式为时间片调度。
rt_thread_priority_table[256]
----------
| 0 |----->线程2 ---> 线程5 --->...
----------
| 1 |
----------
.........
----------
| 253 | ---> 线程100 --->线程3
----------
| 254 |
----------
| 255 | ---> 线程50
----------
2.设置了全局的就绪表和就绪组
设置线程为挂起状态的接口函数有:rt_thread_startup,rt_thread_suspend
rt_thread_startup
thread->stat = RT_THREAD_SUSPEND
/** 设置线程的状态为挂起状态 */
rt_thread_suspend
thread->stat = RT_THREAD_SUSPEND
rt_schedule_remove_thread
设置线程为挂起状态的接口函数有:rt_thread_exit,rt_thread_detach,rt_thread_delete
rt_thread_delete
rt_schedule_remove_thread
thread->stat = RT_THREAD_CLOSE
rt_list_insert_after(&rt_thread_defunct, &(thread->tlist))
rt_thread_detach
rt_schedule_remove_thread
rt_timer_detach
thread->stat = RT_THREAD_CLOSE
rt_object_detach
rt_thread_exit
rt_schedule_remove_thread
thread->stat = RT_THREAD_CLOSE
rt_timer_detach