RT-Thread系列二 认识线程

134 阅读3分钟

本文已参与 [新人创作礼] 活动,一起开启掘金创作之路。

什么是线程

在裸机系统开发中,系统的主体就是在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;
}