美文网首页
RTT笔记-线程

RTT笔记-线程

作者: lissettecarlr | 来源:发表于2021-02-13 13:50 被阅读0次

继续按照程序流程前进,前面提到的时钟初始化,堆初始化都还未跳出板级初始化rt_hw_board_init函数

然后看看rt_hw_board_init函数中剩下的代码

    /* Pin driver initialization is open by default */
#ifdef RT_USING_PIN
    rt_hw_pin_init(); 
#endif

    /* USART driver initialization is open by default */
#ifdef RT_USING_SERIAL
    rt_hw_usart_init();
#endif

    /* Set the shell console output device */
#ifdef RT_USING_CONSOLE
    rt_console_set_device(RT_CONSOLE_DEVICE_NAME);
#endif

    /* Board underlying hardware initialization */
#ifdef RT_USING_COMPONENTS_INIT
    rt_components_board_init();
#endif

也就是分别对引脚、串口、shell进行了初始化,最后rt_components_board_init是对执行添加进自启动的函数,自启动相关前篇文章描述,这里也就不谈了。
这样又回到了componts.c的rtthread_startup函数中,板级初始化之后需要执行三个函数

    rt_hw_board_init();

    /* show RT-Thread version */
    rt_show_version();

    /* timer system initialization */
    rt_system_timer_init();

    /* scheduler system initialization */
    rt_system_scheduler_init();

其中rt_show_version是打印一些系统信息,rt_system_timer_init则是基于系统时钟的定时器初始化。最后就是操作系统的重头线程调度初始化了rt_system_scheduler_init,但这里先不谈调度器,而是看看本文主题--线程,调度器相关下篇再分析。


先从线程结构体入手,便能了解到每个线程所包含的信息,该定义在文件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 */
#endif /*RT_USING_SMP*/

    /* 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)
    /* 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能占用的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 */

    /* light weight process if present */
#ifdef RT_USING_LWP
    void        *lwp;
#endif

    rt_uint32_t user_data;                             /**< private user data beyond this thread */
};
typedef struct rt_thread *rt_thread_t;

如果看了内核对象管理那篇,前几个成员看着肯定熟悉吧,其实就和内核对象结构体struct rt_object成员是一样,最后多了一个线程链表rt_list_t tlist。里面其他成员单独拿出来是没啥说的,在具体使用时就清楚了。

线程相关函数分析

静态创建线程

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);
}
RTM_EXPORT(rt_thread_init);

英文单词能直接表达其意思就不打备注了,其中RTM_EXPORT宏定义是将rt_thread_init函数公开给所以内核代码。可以参考博文
接下里再看看实际的线程初始化函数_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 = 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 *)rt_thread_exit);
#else
    thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter,
                                          (void *)((char *)thread->stack_addr + thread->stack_size - sizeof(rt_ubase_t)),
                                          (void *)rt_thread_exit);
#endif

    /* priority init */
    RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX);
    thread->init_priority    = priority;
    thread->current_priority = priority;

    thread->number_mask = 0;
#if RT_THREAD_PRIORITY_MAX > 32
    thread->number = 0;
    thread->high_mask = 0;
#endif

    /* 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;
#endif /*RT_USING_SMP*/

    /* 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);

    /* initialize signal */
#ifdef RT_USING_SIGNALS
    thread->sig_mask    = 0x00;
    thread->sig_pending = 0x00;

    thread->sig_ret     = RT_NULL;
    thread->sig_vectors = RT_NULL;
    thread->si_list     = RT_NULL;
#endif

#ifdef RT_USING_LWP
    thread->lwp = RT_NULL;
#endif

    RT_OBJECT_HOOK_CALL(rt_thread_inited_hook, (thread));

    return RT_EOK;
}

其中每个线程都建立了一个定时器,超时函数均指向了rt_thread_timeout,他实际上就是将对于的线程踢出挂起链表,加入到就绪链表,并且手动执行调度器切换,在看调度器部分的时候应该会配合则这个函数说明,这里就不多言了。

移除进程函数

该函数与静态创建线程配对,其实静态创建于动态创建区域就在于是在运行之前就分配好了内存,还是在运行中使用malloc来分配,其他部分都是一样的,这里就不再分析动态创建删除等相关的函数了。

rt_err_t rt_thread_detach(rt_thread_t thread)
{
    rt_base_t lock;

    /* thread check */
    RT_ASSERT(thread != RT_NULL);
    RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);
    RT_ASSERT(rt_object_is_systemobject((rt_object_t)thread));

    if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_INIT) //线程被创建但未启动则处于INIT状态
    {
        /* remove from schedule */
        rt_schedule_remove_thread(thread);
    }

    /* release thread timer */
    rt_timer_detach(&(thread->thread_timer)); //删掉线程匹配的定时器

    /* change stat */
    thread->stat = 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_list_insert_after(&rt_thread_defunct, &(thread->tlist));

        /* enable interrupt */
        rt_hw_interrupt_enable(lock);
    }

    return RT_EOK;
}
RTM_EXPORT(rt_thread_detach);

启动线程

rt_thread_resume函数是将挂起线程切换成就绪状态并且放入线程链表中去,在线程启动函数rt_thread_startup中被调用,所以先拿出来说说。
其实线程状态实际就四种,初始、就绪、挂起、关闭。初始就是线程刚刚被创建时候的状态,就绪表示该线程已经准备好被执行,就等调度器乱转到它了,挂起表示该线程在获得某种条件前不被执行。关闭和初始对应,就是线程被删除。

rt_err_t rt_thread_resume(rt_thread_t thread)
{
    register rt_base_t temp;

    /* thread check */
    RT_ASSERT(thread != RT_NULL);
    RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);

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

      //当线程状态不是SUSPEND时,错误退出
    if ((thread->stat & RT_THREAD_STAT_MASK) != 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));

    rt_timer_stop(&thread->thread_timer);

    /* enable interrupt */
    rt_hw_interrupt_enable(temp);

    /* insert to schedule ready list */
        //这里是将状态切换成READY,并且插入到线程链表中去
    rt_schedule_insert_thread(thread);

    RT_OBJECT_HOOK_CALL(rt_thread_resume_hook, (thread));
    return RT_EOK;
}
RTM_EXPORT(rt_thread_resume);
rt_err_t rt_thread_startup(rt_thread_t thread)
{
    /* thread check */
    RT_ASSERT(thread != RT_NULL);
    RT_ASSERT((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_INIT);
    RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);

    /* 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; //状态由INIT 变成了SUSPEND
    /* then resume it */
    rt_thread_resume(thread);//切换成就绪态,加入线程链表
    if (rt_thread_self() != RT_NULL)
    {
        /* do a scheduling */
        rt_schedule();//该函数是手动切换一次调度器轮转,选择一个最高优先级的线程执行
    }

    return RT_EOK;
}
RTM_EXPORT(rt_thread_startup);

线程挂起

与函数rt_thread_resume相对于的是rt_thread_suspend,前者是将挂起转为就绪,后者是将就绪转为挂起。

rt_err_t rt_thread_suspend(rt_thread_t thread)
{
    register rt_base_t stat;
    register rt_base_t temp;

    /* thread check */
    RT_ASSERT(thread != RT_NULL);
    RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);

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

    stat = thread->stat & RT_THREAD_STAT_MASK;
    if ((stat != RT_THREAD_READY) && (stat != RT_THREAD_RUNNING))
    {
        RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread suspend: thread disorder, 0x%2x\n",
                                       thread->stat));
        return -RT_ERROR;
    }

    /* disable interrupt */
    temp = rt_hw_interrupt_disable();
    if (stat == RT_THREAD_RUNNING)
    {
        /* not suspend running status thread on other core */
             //当执行挂起正在运行的程序则会报错,正在运行其实意思就是自己,也就是自己不能挂起自己
        RT_ASSERT(thread == rt_thread_self());
    }

    /* change thread stat */
        //踢出链表,状态切换为suspend
    rt_schedule_remove_thread(thread);
    thread->stat = RT_THREAD_SUSPEND | (thread->stat & ~RT_THREAD_STAT_MASK);

    /* stop thread timer anyway */
    rt_timer_stop(&(thread->thread_timer));

    /* enable interrupt */
    rt_hw_interrupt_enable(temp);

    RT_OBJECT_HOOK_CALL(rt_thread_suspend_hook, (thread));
    return RT_EOK;
}
RTM_EXPORT(rt_thread_suspend);

rt_thread_sleep函数实际上是基于rt_thread_suspend和定时器做了一个功能性封装,先将对于线程挂起,然后开启定时器,由于定时器超时函数被设定成了将对于线程加入就绪链表中去,就达到了类似让线程挂起特定时间的目的。

rt_err_t rt_thread_sleep(rt_tick_t tick)
{
    register rt_base_t temp;
    struct rt_thread *thread;

    /* set to current thread */
    thread = rt_thread_self();
    RT_ASSERT(thread != RT_NULL);
    RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);

    /* disable interrupt */
    temp = rt_hw_interrupt_disable();

    /* 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;
}

其他函数便没什么说的了,接下来便是调度器部分。这里补充说明一下关于线程栈初始化函数rt_hw_stack_init,它实际上是根据不同内核进行的栈信息填充,我们分配给线程的栈在这里被切成了两部分,第一部分是CPU寄存器,主要是用来保存上下文,第二部分才是线程可使用的栈空间。

相关文章

  • RTT笔记-线程

    继续按照程序流程前进,前面提到的时钟初始化,堆初始化都还未跳出板级初始化rt_hw_board_init函数 然后...

  • RTT笔记-线程调度

    承接上文分析完线程,此文研究线程调度,由初始化函数rt_system_scheduler_init开始。 sche...

  • 工欲善其事,必先利其器:通过Telnet访问SEGGER RTT

    Segger提供了RTT Viewer、RTT Client和RTT Logger3个工具用于RTT的访问,下面列...

  • RTT笔记-shell

    RTT笔记-shell 模式切换 finsh同时支持c-c-style模式和msh模式,默认进入msh模式,在该模...

  • RTT笔记-SPI

    该笔记类别主要是在自己学习时做的一些记录,方便自己很久不用忘掉时进行快速回忆 1 简述 和串口不同,SPI属于总线...

  • RTT笔记-package

    该笔记类别主要是在自己学习时做的一些记录,方便自己很久不用忘掉时进行快速回忆 1 简述 被封装用于特定功能的代码,...

  • RTT笔记-进程

    修改!该文变更为快速查阅编程文档,RTT进程实现分析将另起一文 动态创建线程 静态创建线程 事件 消息队列 定义 ...

  • RTT笔记-ulog

    该笔记类别主要是在自己学习时做的一些记录,方便自己很久不用忘掉时进行快速回忆 1 导入组件 代码位置:rt-thr...

  • RTT笔记-AT组件

    1 导入系统 AT 组件中 AT Client 功能占用资源体积为 4.6K ROM 和 2.0K RAM; AT...

  • RTT笔记-fal

    该笔记类别主要是在自己学习时做的一些记录,方便自己很久不用忘掉时进行快速回忆 1 使用 1.1 导入文件 文件名位...

网友评论

      本文标题:RTT笔记-线程

      本文链接:https://www.haomeiwen.com/subject/vatekctx.html