RT-Thread内核源码分析-线程栈结构分析

 

RT-Thread提供了一套满足POSIX标准的接口,因此基于RT-Thread编写的应用程序,可以相对轻松的移植到linux平台。 pthread_create接口用来创建一个线程, 接下来我们基于pthread_create来分析动态分配的数组时如何作为线程栈来使用的。


int pthread_create(pthread_t            *tid,
                   const pthread_attr_t *attr, 
                   void *(*start) (void *), void *parameter)
{
    int result;
    void *stack;
    char name[RT_NAME_MAX];
    static rt_uint16_t pthread_number = 0;
    _pthread_data_t *ptd;

    /* tid shall be provided */
    RT_ASSERT(tid != RT_NULL);

    /* allocate posix thread data */
    ptd = (_pthread_data_t*)rt_malloc(sizeof(_pthread_data_t));
    if (ptd == RT_NULL)
        return ENOMEM;
    /* clean posix thread data memory */
    rt_memset(ptd, 0, sizeof(_pthread_data_t));
    ptd->canceled = 0;
    ptd->cancelstate = PTHREAD_CANCEL_DISABLE;
    ptd->canceltype = PTHREAD_CANCEL_DEFERRED;
    ptd->magic = PTHREAD_MAGIC;

    if (attr != RT_NULL)
        ptd->attr = *attr;
    else 
    {
        /* use default attribute */
        pthread_attr_init(&ptd->attr);
    }

    rt_snprintf(name, sizeof(name), "pth%02d", pthread_number ++);
    if (ptd->attr.stack_base == 0)
    {
        stack = (void*)rt_malloc(ptd->attr.stack_size);//根据用户指定的大小动态分配栈空间
    }
    else
        stack = (void*)(ptd->attr.stack_base);

    if (stack == RT_NULL) 
    {
        rt_free(ptd);

        return ENOMEM;
    }

    /* pthread is a static thread object */
    ptd->tid = (rt_thread_t) rt_malloc(sizeof(struct rt_thread));//分配线程控制块
    if (ptd->tid == RT_NULL)
    {
        if (ptd->attr.stack_base == 0)
            rt_free(stack);
        rt_free(ptd);

        return ENOMEM;
    }

    if (ptd->attr.detachstate == PTHREAD_CREATE_JOINABLE)
    {
        ptd->joinable_sem = rt_sem_create(name, 0, RT_IPC_FLAG_FIFO);
        if (ptd->joinable_sem == RT_NULL)
        {
            if (ptd->attr.stack_base != 0)
                rt_free(stack);
            rt_free(ptd);

            return ENOMEM;
        }
    }
    else
        ptd->joinable_sem = RT_NULL;

    /* set parameter */
    ptd->thread_entry = start;
    ptd->thread_parameter = parameter;

    /* initial this pthread to system *///调用rtthread提供的线程初始化接口
    if (rt_thread_init(ptd->tid, name, pthread_entry_stub, ptd, 
        stack, ptd->attr.stack_size, 
        99 - ptd->attr.priority, 5) != RT_EOK)
    {
        if (ptd->attr.stack_base == 0)
            rt_free(stack);
        if (ptd->joinable_sem != RT_NULL)
            rt_sem_delete(ptd->joinable_sem);
        rt_free(ptd);

        return EINVAL;
    }

    /* set pthread id */
    *tid = ptd->tid;

    /* set pthread cleanup function and ptd data */
    (*tid)->cleanup = _pthread_cleanup;
    (*tid)->user_data = (rt_uint32_t)ptd;

    /* start thread */
    result = rt_thread_startup(*tid);//启动该线程
    if (result == RT_EOK)
        return 0;

    /* start thread failed */
    rt_thread_detach(ptd->tid);
    if (ptd->attr.stack_base == 0)
        rt_free(stack);
    if (ptd->joinable_sem != RT_NULL)
        rt_sem_delete(ptd->joinable_sem);

    rt_free(ptd);

    return EINVAL;
}

pthread_create内部是调用rt_thread_init和rt_thread_startup来完成线程的初始化和启动工作,我们先看初始化函数:

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);
}


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);
    // 传入的栈地址是: stack_addr + stack_size - 4; 之所以这么做是确保栈空间是向下增长的, 注意:这里有一个减4的操作 
    thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter,
        (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;
    thread->remaining_tick = tick;

    /* error and flags */
    thread->error = RT_EOK;
    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_uint8_t *rt_hw_stack_init(void       *tentry,
                             void       *parameter,
                             rt_uint8_t *stack_addr,
                             void       *texit)
{
    struct stack_frame *stack_frame;
    rt_uint8_t         *stk;
    unsigned long       i;

    stk  = stack_addr + sizeof(rt_uint32_t);//这里又多了一个加4的操作, 和上文中的减4的操作配合,这个是为什么呢?
    stk  = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stk, 8);//8字节对齐, 不用担心stk会越界;
    stk -= sizeof(struct stack_frame);//stack_frame是ARM寄存器存储列表,也就是我们栈中寄存器的存储顺序, 这个操作将寄存器入栈, stk即为栈顶指针,接下来是对线程栈内容的初始化

    stack_frame = (struct stack_frame *)stk;

    /* init all register */
    for (i = 0; i < sizeof(struct stack_frame) / sizeof(rt_uint32_t); i ++)
    {
        ((rt_uint32_t *)stack_frame)[i] = 0xdeadbeef;
    }

    stack_frame->exception_stack_frame.r0  = (unsigned long)parameter; /* r0 : argument */
    stack_frame->exception_stack_frame.r1  = 0;                        /* r1 */
    stack_frame->exception_stack_frame.r2  = 0;                        /* r2 */
    stack_frame->exception_stack_frame.r3  = 0;                        /* r3 */
    stack_frame->exception_stack_frame.r12 = 0;                        /* r12 */
    stack_frame->exception_stack_frame.lr  = (unsigned long)texit;     /* lr */
    stack_frame->exception_stack_frame.pc  = (unsigned long)tentry;    /* entry point, pc */
    stack_frame->exception_stack_frame.psr = 0x01000000L;              /* PSR */

    /* return task's current stack address */
    return stk;//返回栈顶指针
}
 

struct exception_stack_frame
{
    rt_uint32_t r0;
    rt_uint32_t r1;
    rt_uint32_t r2;
    rt_uint32_t r3;
    rt_uint32_t r12;
    rt_uint32_t lr;
    rt_uint32_t pc;
    rt_uint32_t psr;
};

struct stack_frame
{
    /* r4 ~ r11 register */
    rt_uint32_t r4;
    rt_uint32_t r5;
    rt_uint32_t r6;
    rt_uint32_t r7;
    rt_uint32_t r8;
    rt_uint32_t r9;
    rt_uint32_t r10;
    rt_uint32_t r11;

    struct exception_stack_frame exception_stack_frame;
};

到这里,我们就清楚了线程在创建时分配的数组是如何转化为线程栈的, 动态分配的数组位于堆上, 数组作为栈来使用时,需要从数组的末尾进行栈的初始化。

    再来看函数rt_thread_startup, 我们知道,在linux环境下,pthread_create函数创建线程后,子线程也会立即被调度执行,那么,在RT-Thead环境下,也是这样的效果吗?

/**
 * 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;
    /* then resume it */
    rt_thread_resume(thread);
    if (rt_thread_self() != RT_NULL)//这里是子线程是否立即被调度执行的关键
    {
        /* do a scheduling */
        rt_schedule();
    }

    return RT_EOK;
}



/**
 * This function will return self thread object
 *
 * @return the self thread object
 */
rt_thread_t rt_thread_self(void)
{
    return rt_current_thread;//全局变量
}

所以,  pthread_create创建的子线程是否立即调度的关键在于全局变量rt_current_thread是否为NULL,我们分析系统启动代码:

void rtthread_startup(void)
{
    /* init board */
    rt_hw_board_init();

#ifdef RT_USING_HEAP
#if STM32_EXT_SRAM
    rt_system_heap_init((void*)STM32_EXT_SRAM_BEGIN, (void*)STM32_EXT_SRAM_END);
#else
#ifdef __CC_ARM
    rt_system_heap_init((void*)&Image$$RW_IRAM1$$ZI$$Limit, (void*)STM32_SRAM_END);
#elif __ICCARM__
    rt_system_heap_init(__segment_end("HEAP"), (void*)STM32_SRAM_END);
#else
    /* init memory system */
    rt_system_heap_init((void*)&__bss_end, (void*)STM32_SRAM_END);
#endif
#endif  /* STM32_EXT_SRAM */
#endif /* RT_USING_HEAP */

    /* init scheduler system */
    rt_system_scheduler_init();

    /* initialize timer */
    rt_system_timer_init();

    /* init timer thread */
    rt_system_timer_thread_init();

#ifdef HW_RTC
    /* init rtc */
    rt_hw_rtc_init();
#endif
    /* init application */
    rt_application_init();//应用程序线程初始化

#ifndef FACTORY_TEST_MODE
    setup_watchdog();
#endif

    /* init idle thread */
    rt_thread_idle_init();// 系统空闲线程初始化,

    /* start scheduler */
    rt_system_scheduler_start();//启动内核调度器

    /* never reach here */
    return ;
}

int main(void)
{
    /* disable interrupt first */
    rt_hw_interrupt_disable();

    /* startup RT-Thread RTOS */
    rtthread_startup();

    return 0;
}



/**
 * @ingroup SystemInit
 * This function will startup scheduler. It will select one thread
 * with the highest priority level, then switch to it.
 */
void rt_system_scheduler_start(void)
{
    register struct rt_thread *to_thread;
    register rt_ubase_t highest_ready_priority;

#if RT_THREAD_PRIORITY_MAX > 32
    register rt_ubase_t number;

    number = __rt_ffs(rt_thread_ready_priority_group) - 1;
    highest_ready_priority = (number << 3) + __rt_ffs(rt_thread_ready_table[number]) - 1;
#else
    highest_ready_priority = __rt_ffs(rt_thread_ready_priority_group) - 1;
#endif

    /* get switch to thread */
    to_thread = rt_list_entry(rt_thread_priority_table[highest_ready_priority].next,
                              struct rt_thread,
                              tlist);

    rt_current_thread = to_thread;//对全局变量进行设置, 相当于内核调度器的启动开关

    /* switch to new thread */
    rt_hw_context_switch_to((rt_uint32_t)&to_thread->sp);

    /* never come back */
}

所以, 在rt_system_scheduler_start调用之前, 通过pthread_create创建的线程是不会被调度的。rt_system_scheduler_start是内核调度器的启动开关,只有在内核调度器启动后,才可通过rt_schedule进行线程切换。

rt_system_scheduler_start 与 rt_schedule的区别可以参考文章https://blog.csdn.net/u011734326/article/details/90173478

 

 

 

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值