本文已参与 [新人创作礼] 活动,一起开启掘金创作之路。
什么是线程
在裸机系统开发中,系统的主体就是在main函数里面顺序执行的无限循环,在这个无限循环中,CPU按照顺序完成各种操作。在多线程系统中,根据功能的不同,把整个系统分割成一个个独立的且无法返回的函数,这个函数就成为线程,也可以称其为任务。
线程创建
定义线程栈
栈是单片机RAM里的一段连续的内存空间,栈的大小一般在启动文件或者链接脚本中指定,最后由C库函数__main进行初始化。
在多线程系统中,每个线程都是独立的、互不干扰的,所以要为每个线程都分配独立的栈空间。这个栈空间通常是一个预先定义好的全局数组,也可以是动态分配的一段内存空间。无论是哪种情况,栈空间都存在于RAM中。
定义线程栈的示例代码如下:
ALIGN(RT_ALIGN_SIZE)
rt_uint8_t thread1_stack[512];
rt_uint8_t thread2_stack[1024];
定义线程函数
线程是一个独立的函数,函数主体无限循环且不能返回。如下代码所示:
void thread1_entry(void *arg)
{
while(1)
{
flag1 = 1;
delay(100);
flag1 = 0;
delay(100);
}
}
void thread2_entry(void *arg)
{
while(1)
{
flag2 = 0;
delay(100);
flag2 = 1;
delay(100);
}
}
定义线程控制块
在裸机系统中,程序的主体是CPU按照顺序执行的;而在多线程系统中,线程的执行则是由系统调度的。系统为了顺利地调度线程,会为每个线程单独定义一个线程控制块,这个线程控制块相当于线程的“身份证”,里边存有线程的所有信息。有了线程控制块这个“identifier”,系统以后对线程的全部操作都由其来实现。线程控制块在rtdef.h中声明,使用它可以为每个线程都定义一个线程控制块实体,代码如下所示:
/**
* Thread structure
*/
struct rt_thread
{
/* rt object */
char name[RT_NAME_MAX]; /**< the name of thread */
rt_uint8_t type; /**< type of object */
rt_uint8_t flags; /**< thread's flags */
#ifdef RT_USING_MODULE
void *module_id; /**< id of application module */
#endif
rt_list_t list; /**< the object list */
rt_list_t tlist; /**< the thread list */
/* stack point and entry */
void *sp; /**< stack point */
void *entry; /**< entry */
void *parameter; /**< parameter */
void *stack_addr; /**< stack address */
rt_uint32_t stack_size; /**< stack size */
/* error code */
rt_err_t error; /**< error code */
rt_uint8_t stat; /**< thread status */
#ifdef RT_USING_SMP
rt_uint8_t bind_cpu; /**< thread is bind to cpu */
rt_uint8_t oncpu; /**< process on cpu */
rt_uint16_t scheduler_lock_nest; /**< scheduler lock count */
rt_uint16_t cpus_lock_nest; /**< cpus lock count */
rt_uint16_t critical_lock_nest; /**< critical lock count */
#endif /*RT_USING_SMP*/
/* priority */
rt_uint8_t current_priority; /**< current priority */
#if RT_THREAD_PRIORITY_MAX > 32
rt_uint8_t number;
rt_uint8_t high_mask;
#endif
rt_uint32_t number_mask;
#if defined(RT_USING_EVENT)
/* thread event */
rt_uint32_t event_set;
rt_uint8_t event_info;
#endif
#if defined(RT_USING_SIGNALS)
rt_sigset_t sig_pending; /**< the pending signals */
rt_sigset_t sig_mask; /**< the mask bits of signal */
#ifndef RT_USING_SMP
void *sig_ret; /**< the return stack pointer from signal */
#endif
rt_sighandler_t *sig_vectors; /**< vectors of signal handler */
void *si_list; /**< the signal infor list */
#endif
rt_ubase_t init_tick; /**< thread's initialized tick */
rt_ubase_t remaining_tick; /**< remaining tick */
#ifdef RT_USING_CPU_USAGE
rt_uint64_t duration_tick; /**< cpu usage tick */
#endif
struct rt_timer thread_timer; /**< built-in thread timer */
void (*cleanup)(struct rt_thread *tid); /**< cleanup function when thread exit */
/* light weight process if present */
#ifdef RT_USING_LWP
void *lwp;
#endif
rt_ubase_t user_data; /**< private user data beyond this thread */
};
typedef struct rt_thread *rt_thread_t;
定义线程控制块的代码如下所示:
struct rt_thread thread1;
struct rt_thread thread2;
实现线程创建函数
线程的栈、函数实体和线程控制块最终需要联系起来才能由系统进行统一调度,这个联系的工作就由线程初始化函数rt_thread_init函数来实现。rt_thread_init函数在thread.c(src\thread.c)中实现,在thread.h(include\rtthread.h)中声明。函数实现代码如下所示:
static rt_err_t _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)
{
/* init thread list */
rt_list_init(&(thread->tlist));
thread->entry = (void *)entry;
thread->parameter = parameter;
/* stack init */
thread->stack_addr = stack_start;
thread->stack_size = stack_size;
/* init thread stack */
rt_memset(thread->stack_addr, '#', thread->stack_size);
#ifdef ARCH_CPU_STACK_GROWS_UPWARD
thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter,
(void *)((char *)thread->stack_addr),
(void *)_thread_exit);
#else
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 *)_thread_exit);
#endif /* ARCH_CPU_STACK_GROWS_UPWARD */
/* priority init */
RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX);
thread->current_priority = priority;
thread->number_mask = 0;
#ifdef RT_USING_EVENT
thread->event_set = 0;
thread->event_info = 0;
#endif
#if RT_THREAD_PRIORITY_MAX > 32
thread->number = 0;
thread->high_mask = 0;
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
/* tick init */
thread->init_tick = tick;
thread->remaining_tick = tick;
/* error and flags */
thread->error = RT_EOK;
thread->stat = RT_THREAD_INIT;
#ifdef RT_USING_SMP
/* not bind on any cpu */
thread->bind_cpu = RT_CPUS_NR;
thread->oncpu = RT_CPU_DETACHED;
/* lock init */
thread->scheduler_lock_nest = 0;
thread->cpus_lock_nest = 0;
thread->critical_lock_nest = 0;
#endif /* RT_USING_SMP */
/* initialize cleanup function and user data */
thread->cleanup = 0;
thread->user_data = 0;
/* initialize thread timer */
rt_timer_init(&(thread->thread_timer),
thread->name,
_thread_timeout,
thread,
0,
RT_TIMER_FLAG_ONE_SHOT);
/* initialize signal */
#ifdef RT_USING_SIGNALS
thread->sig_mask = 0x00;
thread->sig_pending = 0x00;
#ifndef RT_USING_SMP
thread->sig_ret = RT_NULL;
#endif /* RT_USING_SMP */
thread->sig_vectors = RT_NULL;
thread->si_list = RT_NULL;
#endif /* RT_USING_SIGNALS */
#ifdef RT_USING_LWP
thread->lwp = RT_NULL;
#endif /* RT_USING_LWP */
#ifdef RT_USING_CPU_USAGE
thread->duration_tick = 0;
#endif
#ifdef RT_USING_MODULE
thread->module_id = 0;
#endif
thread->user_data = 0;
RT_OBJECT_HOOK_CALL(rt_thread_inited_hook, (thread));
return RT_EOK;
}