欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

rt-thread线程源码分析

程序员文章站 2022-07-11 18:54:31
...

rt-thread操作系统是一个多线程的操作系统,线程对于rt-thread来说是一个很重要的概念,因此,必须掌握它。

1 线程控制块的数据结构

/**
 * Thread structure
 */
struct rt_thread
{
    /* rt object *///这里就是rt_object的结构,其实也可以用rt_object parent来定义,估计线程在早些时候并没有这么做,后来也就没改过来
    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//模块ID
    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_uint16_t stack_size;                             /**< stack size *///栈大小

    /* error code */
    rt_err_t    error;                                  /**< error code *///错误代码,用于IPC机制中,标志是否已经获取成功

    rt_uint8_t  stat;                                   /**< thread stat *///线程的当前状态

    /* priority */
    rt_uint8_t  current_priority;                       /**< current priority *///当前优先级
    rt_uint8_t  init_priority;                          /**< initialized 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)//与IPC机制事件相关的一些参数
    /* thread event */
    rt_uint32_t event_set; //此线程接收到的事件
    rt_uint8_t  event_info;//此线程的事件过滤信息,用于过滤事件,只保留感兴趣的事件
#endif

    rt_ubase_t  init_tick;                              /**< thread's initialized tick *///初始tick
    rt_ubase_t  remaining_tick;                         /**< remaining tick *///剩余tick

    struct rt_timer thread_timer;                       /**< built-in thread timer *///线程定时器

    void (*cleanup)(struct rt_thread *tid);             /**< cleanup function when thread exit *///相当于线程的析构函数,用于销毁线程时做些后续操作

    rt_uint32_t user_data;                              /**< private user data beyond this thread *///析构函数的输入参数
};
typedef struct rt_thread *rt_thread_t;

上面代码其中线程控制块的内部成员number, high_mask, number_mask与线程调度时获获取当前最高优先级线程的算法有关,这里不做介绍,详情请见:http://blog.csdn.net/flydream0/article/details/8588584

event_set, evernt_info与事件相关,在后续讲到IPC机制的事件时将会提出,这里也先不做介绍.

2 线程创建及初始化

2.1 初始化线程

/*@{*/

/**
 * This function will initialize a thread, normally it's used to initialize a
 * static thread object.
 *
 * @param thread the static thread object
 * @param name the name of thread, which shall be unique
 * @param entry the entry function of thread
 * @param parameter the parameter of thread enter function
 * @param stack_start the start address of thread stack
 * @param stack_size the size of thread stack
 * @param priority the priority of thread
 * @param tick the time slice if there are same priority thread
 *
 * @return the operation status, RT_EOK on OK, -RT_ERROR on error
 */
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)
{
    /* thread check *///参数检查
    RT_ASSERT(thread != RT_NULL);
    RT_ASSERT(stack_start != RT_NULL);

    /* init thread object */
    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的函数如下定义:
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)
{
    /* 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 = (rt_uint16_t)stack_size;//栈大小

    /* init thread stack */
    rt_memset(thread->stack_addr, '#', thread->stack_size);//将栈内的所有字节初始化为'#'号
    thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter,//初始化时设置sp的内容,rt_hw_stack_init是一个与具体MCU相关的函数,这里就不做介绍
        (void *)((char *)thread->stack_addr + thread->stack_size - 4),
        (void *)rt_thread_exit);

    /* priority init */
    RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX);
    thread->init_priority    = priority;//当前优先级和初始化优先级设置
    thread->current_priority = priority;

    /* tick init */
    thread->init_tick      = tick;//初始化tick和剩余tick
    thread->remaining_tick = tick;

    /* error and flags */
    thread->error = RT_EOK;//错误的状态,线程状态初始化时为RT_THREAD_INIT
    thread->stat  = RT_THREAD_INIT;

    /* initialize cleanup function and user data */
    thread->cleanup   = 0;//线程析构函数及其参数
    thread->user_data = 0;

    /* init thread timer */
    rt_timer_init(&(thread->thread_timer),//初始化线程的定时器
                  thread->name,
                  rt_thread_timeout,
                  thread,
                  0,
                  RT_TIMER_FLAG_ONE_SHOT);

    return RT_EOK;
}

初始化函数将线程栈内容全部初始化为'#'号.

其中rt_thread_timeout的函数如下定义:

/**
 * This function is the timeout function for thread, normally which is invoked
 * when thread is timeout to wait some resource.
 *
 * @param parameter the parameter of thread timeout function
 */
void rt_thread_timeout(void *parameter)
{
    struct rt_thread *thread;

    thread = (struct rt_thread *)parameter;

    /* thread check */
    RT_ASSERT(thread != RT_NULL);
    RT_ASSERT(thread->stat == RT_THREAD_SUSPEND);

    /* set error number */
    thread->error = -RT_ETIMEOUT;//设置此线程的error为超时错误,这些IPC机制中非常有用,

    /* remove from suspend list *///从挂起链表中移除
    rt_list_remove(&(thread->tlist));

    /* insert to schedule ready list */
    rt_schedule_insert_thread(thread);//加入调度器

    /* do schedule */
    rt_schedule();//重新调度
}

注:当线程进入睡眠时,程序将线程对应的定时器加入到定时器超时链表,一旦时间到达,则调用此定时器的超时处理函数,即rt_thread_timeout函数,可上源码可知,在这个线程定时器超时处理函数内,将会将线程加入到调度器。

此外,需要特别注意地是,此函数会将超时的线程的error设置为-RT_ETIMEOUT,用来标志此线程并未获得IPC,这在IPC机制中判断某个线程是否已成功获取某个IPC对象时非常有用。

此超时回调函数主要是将挂起的线程加入到调度器中进行重新调度,即唤醒它。

2.2 创建线程

/**
 * This function will create a thread object and allocate thread object memory
 * and stack.
 *
 * @param name the name of thread, which shall be unique
 * @param entry the entry function of thread
 * @param parameter the parameter of thread enter function
 * @param stack_size the size of thread stack
 * @param priority the priority of thread
 * @param tick the time slice if there are same priority thread
 *
 * @return the created thread object
 */
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_malloc(stack_size);//动态分配一个线程栈
    if (stack_start == RT_NULL)
    {
        /* allocate stack failure */
        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;
}

3 线程的脱离及删除

3.1 脱离线程

/**
 * This function will detach a thread. The thread object will be removed from
 * thread queue and detached/deleted from system object management.
 *
 * @param thread the thread to be deleted
 *
 * @return the operation status, RT_EOK on OK, -RT_ERROR on error
 */
rt_err_t rt_thread_detach(rt_thread_t thread)
{
    rt_base_t lock;

    /* thread check */
    RT_ASSERT(thread != RT_NULL);

    /* remove from schedule */
    rt_schedule_remove_thread(thread);//将线程从调度器中移除

    /* release thread timer */
    rt_timer_detach(&(thread->thread_timer));//脱离定时器

    /* change stat */
    thread->stat = RT_THREAD_CLOSE;//将线程的状态设置为RT_THREAD_CLOSE

    /* detach object */
    rt_object_detach((rt_object_t)thread);//脱离内核对象

    if (thread->cleanup != RT_NULL)//如果存在线程析构函数
    {
        /* disable interrupt */
        lock = rt_hw_interrupt_disable();//关中断

        /* insert to defunct thread list *///rt_thread_defunct链表在系统空闲时将被空闲线程来处理
        rt_list_insert_after(&rt_thread_defunct, &(thread->tlist));//将线程加入到rt_thread_defunct链表中

        /* enable interrupt */
        rt_hw_interrupt_enable(lock);//开中断
    }

    return RT_EOK;
}

需要注意地是,线程的脱离函数如果当前线程离开进行一些善后工作,即存在cleanup析构函数,此时,会将此线程加入到回收线程链表rt_thread_defunct中去,等到系统空闲时再由空闲线程来“回收"此线程,详情请参考:http://blog.csdn.net/flydream0/article/details/8590415 一文.

3.2 删除线程

/**
 * This function will delete a thread. The thread object will be removed from
 * thread queue and detached/deleted from system object management.
 *
 * @param thread the thread to be deleted
 *
 * @return the operation status, RT_EOK on OK, -RT_ERROR on error
 */
rt_err_t rt_thread_delete(rt_thread_t thread)
{
    rt_base_t lock;

    /* thread check */
    RT_ASSERT(thread != RT_NULL);

    /* remove from schedule */
    rt_schedule_remove_thread(thread);//从调度器中移除线程

    /* release thread timer */
    rt_timer_detach(&(thread->thread_timer));//脱离定时器

    /* change stat */
    thread->stat = RT_THREAD_CLOSE;//线程状态设置为RT_THREAD_CLOSE

    /* disable interrupt */
    lock = rt_hw_interrupt_disable();//关中断

    /* insert to defunct thread list */
    rt_list_insert_after(&rt_thread_defunct, &(thread->tlist));//将当前线程加入到空闲时才会处理的链表中

    /* enable interrupt */
    rt_hw_interrupt_enable(lock);//开中断

    return RT_EOK;
}

4 启动线程

/**
 * This function will start a thread and put it to system ready queue
 *
 * @param thread the thread to be started
 *
 * @return the operation status, RT_EOK on OK, -RT_ERROR on error
 */
rt_err_t rt_thread_startup(rt_thread_t thread)
{
    /* thread check *///参数检查
    RT_ASSERT(thread != RT_NULL);
    RT_ASSERT(thread->stat == RT_THREAD_INIT);

    /* set current priority to init priority */
    thread->current_priority = thread->init_priority;//启动线程时将线程当前的优先级设置为初始优先级

    /* calculate priority attribute */
#if RT_THREAD_PRIORITY_MAX > 32
    thread->number      = thread->current_priority >> 3;            /* 5bit */
    thread->number_mask = 1L << thread->number;
    thread->high_mask   = 1L << (thread->current_priority & 0x07);  /* 3bit */
#else
    thread->number_mask = 1L << thread->current_priority;
#endif

    RT_DEBUG_LOG(RT_DEBUG_THREAD, ("startup a thread:%s with priority:%d\n",
                                   thread->name, thread->init_priority));
    /* change thread stat */
    thread->stat = RT_THREAD_SUSPEND;//将线程的状态设置为RT_THREAD_SUSPEND
    /* then resume it */
    rt_thread_resume(thread);//还原线程
    if (rt_thread_self() != RT_NULL)//如果当前的线程不为空,则执行线程调度操作
    {
        /* do a scheduling */
        rt_schedule();
    }

    return RT_EOK;
}

由此可见,启动线程时,首先会将线程设置为挂起状态,然后再唤醒它。

其中rt_thread_self函数为获取当前线程,其源码如下定义:

/**
 * This function will return self thread object
 *
 * @return the self thread object
 */
rt_thread_t rt_thread_self(void)
{
    return rt_current_thread;
}
rt_current_thread为全局变量,保存当前正在运行的线程。

rt_thread_resume函数见后第6章内容,rt_schedule函数见线程调度源码分析相关章节.

5 线程挂起

/**
 * This function will suspend the specified thread.
 *
 * @param thread the thread to be suspended
 *
 * @return the operation status, RT_EOK on OK, -RT_ERROR on error
 *
 * @note if suspend self thread, after this function call, the
 * rt_schedule() must be invoked.
 */
rt_err_t rt_thread_suspend(rt_thread_t thread)
{
    register rt_base_t temp;

    /* thread check */
    RT_ASSERT(thread != RT_NULL);

    RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread suspend:  %s\n", thread->name));

    if (thread->stat != RT_THREAD_READY)//此函数只对处于就绪状态的线程操作
    {
        RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread suspend: thread disorder, %d\n",
                                       thread->stat));
        
        return -RT_ERROR;
    }

    /* disable interrupt */
    temp = rt_hw_interrupt_disable();//关中断

    /* change thread stat */
    thread->stat = RT_THREAD_SUSPEND;//将线程设置为挂起状态
    rt_schedule_remove_thread(thread);//将线程从调试器中移除

    /* enable interrupt */
    rt_hw_interrupt_enable(temp);//开中断

    return RT_EOK;
}

有关rt_schedule_remove_thread函数见后续在前调度器源码分析的文章。

此函数比较简单。

6 线程唤醒

/**
 * This function will resume a thread and put it to system ready queue.
 *
 * @param thread the thread to be resumed
 *
 * @return the operation status, RT_EOK on OK, -RT_ERROR on error
 */
rt_err_t rt_thread_resume(rt_thread_t thread)
{
    register rt_base_t temp;

    /* thread check */
    RT_ASSERT(thread != RT_NULL);

    RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread resume:  %s\n", thread->name));

    if (thread->stat != RT_THREAD_SUSPEND)//只对处于挂起的线程进行还原操作
    {
        RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread resume: thread disorder, %d\n",
                                       thread->stat));

        return -RT_ERROR;
    }

    /* disable interrupt */
    temp = rt_hw_interrupt_disable();//关中断

    /* remove from suspend list */
    rt_list_remove(&(thread->tlist));//从挂起队列中移除

    /* remove thread timer */
    rt_list_remove(&(thread->thread_timer.list));//因线程即将运行,所以需要移除定时器,无需再定时

    /* change timer state */
    thread->thread_timer.parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;//将内核对象的标志设置为定时器非**标志

    /* enable interrupt */
    rt_hw_interrupt_enable(temp);//开中断

    /* insert to schedule ready list */
    rt_schedule_insert_thread(thread);//将线程加入调度器

    return RT_EOK;
}
由上源码可见,此函数只是将线程加入到调度器就绪队列中,并没有真正唤醒它,而真正唤醒线程需要rt_schedule.

7 线程让出处理机

当前线程的时间片用完或者该线程自动要求让出处理器资源时,它不再占有处理机,调度器会选择下一个最高优先级的线程执行。这时,放弃处理器资源的线程仍然在就绪队列中,只不过放到就绪队列末尾去 了.

/**
 * This function will let current thread yield processor, and scheduler will
 * choose a highest thread to run. After yield processor, the current thread
 * is still in READY state.
 *
 * @return RT_EOK
 */
rt_err_t rt_thread_yield(void)
{
    register rt_base_t level;
    struct rt_thread *thread;

    /* disable interrupt */
    level = rt_hw_interrupt_disable();//关中断

    /* set to current thread */
    thread = rt_current_thread;//得到当前线程

    /* if the thread stat is READY and on ready queue list */
    if (thread->stat == RT_THREAD_READY &&//如果当前线程处于就绪状态且在就绪队列
        thread->tlist.next != thread->tlist.prev)
    {
        /* remove thread from thread list */
        rt_list_remove(&(thread->tlist));//从就绪队列中移除当前线程

        /* put thread to end of ready queue */
        rt_list_insert_before(&(rt_thread_priority_table[thread->current_priority]),//加入到就绪队列末尾
                              &(thread->tlist));

        /* enable interrupt */
        rt_hw_interrupt_enable(level);//开中断

        rt_schedule();//重新调度线程

        return RT_EOK;
    }

    /* enable interrupt */
    rt_hw_interrupt_enable(level);//开中断

    return RT_EOK;
}
此函数用于线程的时间片耗尽时,就此线程挂起后加入到就绪队列的末端,然后再等待下一次调度。

8 线程睡眠

/**
 * This function will let current thread sleep for some ticks.
 *
 * @param tick the sleep ticks
 *
 * @return RT_EOK
 */
rt_err_t rt_thread_sleep(rt_tick_t tick)
{
    register rt_base_t temp;
    struct rt_thread *thread;

    /* disable interrupt */
    temp = rt_hw_interrupt_disable();//关中断
    /* set to current thread */
    thread = rt_current_thread;//得到当前线程
    RT_ASSERT(thread != RT_NULL);

    /* suspend thread */
    rt_thread_suspend(thread);//挂起当前线程

    /* reset the timeout of thread timer and start it */
    rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &tick);//设置定时器
    rt_timer_start(&(thread->thread_timer));//启动定时器

    /* enable interrupt */
    rt_hw_interrupt_enable(temp);//开中断

    rt_schedule();//启动调度器

    /* clear error number of this thread to RT_EOK */
    if (thread->error == -RT_ETIMEOUT)//将当前线程的错误码设置为超时
        thread->error = RT_EOK;

    return RT_EOK;
}

/**
 * This function will let current thread delay for some ticks.
 *
 * @param tick the delay ticks
 *
 * @return RT_EOK
 */
rt_err_t rt_thread_delay(rt_tick_t tick)
{
    return rt_thread_sleep(tick);
}
此函数是将当前线程挂起后,然后开启线程中的定时器,并等待定时器时间到达,一旦到达,定时器超时回调函数中将会将此线程重新加入到就绪队列,并重新调度。见2.1节的rt_thread_timeout函数实现部分。

9 线程控制

/**
 * This function will control thread behaviors according to control command.
 *
 * @param thread the specified thread to be controlled
 * @param cmd the control command, which includes
 *  RT_THREAD_CTRL_CHANGE_PRIORITY for changing priority level of thread;
 *  RT_THREAD_CTRL_STARTUP for starting a thread;
 *  RT_THREAD_CTRL_CLOSE for delete a thread.
 * @param arg the argument of control command
 *
 * @return RT_EOK
 */
rt_err_t rt_thread_control(rt_thread_t thread, rt_uint8_t cmd, void *arg)
{
    register rt_base_t temp;

    /* thread check */
    RT_ASSERT(thread != RT_NULL);

    switch (cmd)
    {
    case RT_THREAD_CTRL_CHANGE_PRIORITY://修改优先级
        /* disable interrupt */
        temp = rt_hw_interrupt_disable();//关中断

        /* for ready thread, change queue */
        if (thread->stat == RT_THREAD_READY)//如果线程处于就绪状态
        {
            /* remove thread from schedule queue first */
            rt_schedule_remove_thread(thread);//移除

            /* change thread priority */
            thread->current_priority = *(rt_uint8_t *)arg;//设置优先级

            /* recalculate priority attribute */
#if RT_THREAD_PRIORITY_MAX > 32
            thread->number      = thread->current_priority >> 3;            /* 5bit */
            thread->number_mask = 1 << thread->number;
            thread->high_mask   = 1 << (thread->current_priority & 0x07);   /* 3bit */
#else
            thread->number_mask = 1 << thread->current_priority;
#endif

            /* insert thread to schedule queue again */
            rt_schedule_insert_thread(thread);//加入调度器
        }
        else
        {
            thread->current_priority = *(rt_uint8_t *)arg;

            /* recalculate priority attribute */
#if RT_THREAD_PRIORITY_MAX > 32
            thread->number      = thread->current_priority >> 3;            /* 5bit */
            thread->number_mask = 1 << thread->number;
            thread->high_mask   = 1 << (thread->current_priority & 0x07);   /* 3bit */
#else
            thread->number_mask = 1 << thread->current_priority;
#endif
        }

        /* enable interrupt */
        rt_hw_interrupt_enable(temp);
        break;

    case RT_THREAD_CTRL_STARTUP://启动
        return rt_thread_startup(thread);

#ifdef RT_USING_HEAP
    case RT_THREAD_CTRL_CLOSE://关闭线程
        return rt_thread_delete(thread);
#endif

    default:
        break;
    }

    return RT_EOK;
}
此函数在修改线程优先级时,当线程处于就绪状态时,为了安全起见,首先将线程从就绪队列中移除,然后再修改优先级,最后再次线程重新加入到调度器的就绪队列中。

10 查找线程

/**
 * This function will find the specified thread.
 *
 * @param name the name of thread finding
 *
 * @return the found thread
 *
 * @note please don't invoke this function in interrupt status.
 */
rt_thread_t rt_thread_find(char *name)
{
    struct rt_object_information *information;
    struct rt_object *object;
    struct rt_list_node *node;

    extern struct rt_object_information rt_object_container[];

    /* enter critical */
    if (rt_thread_self() != RT_NULL)
        rt_enter_critical();//进入临界区

    /* try to find device object */
    information = &rt_object_container[RT_Object_Class_Thread];//从内核对象容器中获取内核对象链表
    for (node  = information->object_list.next;
         node != &(information->object_list);
         node  = node->next)
    {
        object = rt_list_entry(node, struct rt_object, list);//得到内核对象
        if (rt_strncmp(object->name, name, RT_NAME_MAX) == 0)//比较名字
        {
            /* leave critical */
            if (rt_thread_self() != RT_NULL)
                rt_exit_critical();//退出临界区

            return (rt_thread_t)object;//返回内核对象
        }
    }

    /* leave critical */
    if (rt_thread_self() != RT_NULL)
        rt_exit_critical();//退出临界区

    /* not found */
    return RT_NULL;//返回未找到
}

查找线程是通过内核对象管理系统来查找的,根据内核对象的类型,找到相应内核对象链表,并遍历它,比较名字,如果找到则返回。

需要注意的是,这里的进入临界区的功能只是让调度器暂时停止工作,即停止调度线程,而退出临界区则是让停止工作的调度器重新恢复工作。这样做的理由是防止临界区内的执行调度器终止,切换到其它线程去了。