rtthread:"rt_thread"线程

1 微处理器系统

  

  随着产品功能的增多,裸机系统不能够满足产品需求,引入RTOS实时操作系统的多线程管理,可以增加程序的稳定性逻辑性,便于管理;

2 rtos系统启动

  正常的系统都是从main函数中启动,那么对于rtos而言。它是如何启动的呢?

  rtos使用了keil编译器的打补丁功能,将thread启动之前的初始化放在了打补丁函数中来初始化,初始化完毕之后调度启动执行;

//components.c
extern int $Super$$main(void);
/* re-define main function */
int $Sub$$main(void)
{
    rt_hw_interrupt_disable();
    rtthread_startup();
    return 0;
}

//这是main函数的补丁操作范围
int rtthread_startup(void)
{
    rt_hw_interrupt_disable();

    rt_hw_board_init();             //NOTE: please initialize heap inside board initialization
    //rt_show_version();
    rt_system_timer_init();         //system:rt_timer_list
    rt_system_scheduler_init();     //system:rt_thread_priority_table

#ifdef RT_USING_SIGNALS
    rt_system_signal_init();
#endif

    rt_application_init();          //main_thread   >>main_thread_entry 
    rt_system_timer_thread_init();  //timer_thread  >>rt_thread_timer_entry
    rt_thread_idle_init();          //idle          >>rt_thread_idle_entry
    rt_system_scheduler_start();    //启动调度函数switch_to汇编;
	
    /* never reach here */
    return 0;
}
void rt_application_init(void)
{
    rt_thread_t tid;

#ifdef RT_USING_HEAP
    tid = rt_thread_create("main", main_thread_entry, RT_NULL,
                           RT_MAIN_THREAD_STACK_SIZE, RT_THREAD_PRIORITY_MAX / 3, 20);
    RT_ASSERT(tid != RT_NULL);
#else
    rt_err_t result;

    tid = &main_thread;
    result = rt_thread_init(tid, "main", main_thread_entry, RT_NULL,
                            main_stack, sizeof(main_stack), RT_THREAD_PRIORITY_MAX / 3, 20);
    RT_ASSERT(result == RT_EOK);
	
    /* if not define RT_USING_HEAP, using to eliminate the warning */
    (void)result;
#endif

    rt_thread_startup(tid);//将"main"线程挂载到优先级表上;优先级=RT_THREAD_PRIORITY_MAX / 3;
}
void main_thread_entry(void *parameter)
{
    extern int main(void);
    extern int $Super$$main(void);

    rt_components_init();

    /* invoke system main function */
#if defined (__CC_ARM)
    $Super$$main();    //回调main函数;
#elif defined(__ICCARM__) || defined(__GNUC__)
    main();
#endif
}

3 线程 thread

  rtos启动完毕之后,我们就可以在main函数中初始化应用层线程了;那什么是线程呢?

  线程是以功能划分的程序单元,该程序单元的所有参数囊括在唯一对应的线程结构体中;thread在其他rtos中也被称为task;

  3.1 线程结构体 rt_thread

    线程结构体存储当前线程的所有参数;对于线程的架构理解可以以线程结构体作为线索来追踪;

//rtdef.h
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 */

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

    void            *sig_ret;                           /**< the return stack pointer from signal */
    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 */

    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;

  3.2 对象 rt_object

    对象是对线程的各类数据结构的统称;

    可以通过容器对象来判断malloc对象分配;但是封装后的数据结构可以挂载到容器上进行统一管理;至于统一管理有啥用还没凸显出来先放着;

//rtdef.h
struct rt_object
{
    char       name[RT_NAME_MAX];                       /**< name of kernel object */
    rt_uint8_t type;                                    /**< type of kernel object */
    rt_uint8_t flag;                                    /**< flag of kernel object */

#ifdef RT_USING_MODULE
    void      *module_id;                               /**< id of application module */
#endif
    rt_list_t  list;                                    /**< list node of kernel object */
};
typedef struct rt_object *rt_object_t;                  /**< Type for kernel objects. */

//rt_object_t->type的取值;
enum rt_object_class_type
{
     RT_Object_Class_Thread = 0,       /* 对象是线程 */
     RT_Object_Class_Semaphore,        /* 对象是信号量 */
     RT_Object_Class_Mutex,            /* 对象是互斥量 */
     RT_Object_Class_Event,            /* 对象是事件 */
     RT_Object_Class_MailBox,          /* 对象是邮箱 */
     RT_Object_Class_MessageQueue,     /* 对象是消息队列 */
     RT_Object_Class_MemHeap,          /* 对象是内存堆 */
     RT_Object_Class_MemPool,          /* 对象是内存池 */
     RT_Object_Class_Device,           /* 对象是设备 */
     RT_Object_Class_Timer,            /* 对象是定时器 */
     RT_Object_Class_Module,           /* 对象是模块 */
     RT_Object_Class_Unknown,          /* 对象未知 */
     RT_Object_Class_Static = 0x80     /* 对象是静态对象 */
};

    3.2.1 容器

      容器是一个可以寻址rtos系统全部对象的数组;

      每个对象在初始化完自己的结构体之后,就顺便把自己挂载到容器对应的数据结构下; 

//容器对象结构体  rtdef.h
struct rt_object_information
{
    enum rt_object_class_type type;            //对象的数据类型
    rt_list_t                 object_list;     //该数据类型挂载list的链表头
    rt_size_t                 object_size;     //该object struct结构体大小
};

//容器对象类型 object.c 
enum rt_object_info_type
{
    RT_Object_Info_Thread = 0,     //线程对象
#ifdef RT_USING_SEMAPHORE
    RT_Object_Info_Semaphore,      //信号量对象
#endif
    //省略...#ifdef ...#endif
#ifdef RT_USING_MODULE
    RT_Object_Info_Module,         //模块对象
#endif
    RT_Object_Info_Unknown,        //对象未知
};

//容器数组  object.c
//下面这行define是用自身地址初始化了容器链表头;
#define _OBJ_CONTAINER_LIST_INIT(c)    {&(rt_object_container[c].object_list), &(rt_object_container[c].object_list)}		
static struct rt_object_information rt_object_container[RT_Object_Info_Unknown] =
{
    {
        RT_Object_Class_Thread, 
        _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Thread), 
        sizeof(struct rt_thread)
    },
		
#ifdef RT_USING_SEMAPHORE
    {
        RT_Object_Class_Semaphore, 
        _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Semaphore), 
        sizeof(struct rt_semaphore)
    },
#endif		
//#ifdef .......
#ifdef RT_USING_MODULE
    {
        RT_Object_Class_Module, 
        _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Module), 
        sizeof(struct rt_module)
    },
#endif
		
};

  3.3 tlist节点

    tlist节点用来将线程挂载到线程就绪优先级表上等待执行,或挂载到系统定时器链表上等待延时到期;

////rtdef.h  注意rt_list_t的第一个节点不是prev,而是next;
struct rt_list_node
{
    struct rt_list_node *next;              
    struct rt_list_node *prev;              
};
typedef struct rt_list_node rt_list_t;

    3.3.1 节点地址反推结构体首地址

//rtservice.h  已知一个结构体里面的节点地址,反推出该节点所在的结构体首地址


#define rt_container_of(ptr, type, member)     ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))		
#define rt_list_entry(node, type, member)       rt_container_of(node, type, member)
//简单整理了一下上面的宏变成下面这样方便理解;只有最外层和node的括号可以去,其他括号都有用;
//#define  rt_list_entry(node, struct, member)   (struct *)(   (char *)node - (unsigned long)( &((struct *)0)->member )   )

/***
1.
    #define 默认换行符为结束define,通过\连接符来连接两行为一个define;
2.
    to_thread = rt_list_entry(rt_thread_priority_table[0].next,  struct rt_thread,  tlist);
              = ( (rt_thread *)((char *)rt_thread_priority_table[0].next - (unsigned long)(&((rt_thread *)0)->tlist)) )
             
    (unsigned long)(&((rt_thread *)0)->tlist)):定义一个rt_thread结构体,初始地址赋值为0,那么&(rt_thread->tlist)就是tlist在结构体中的偏移地址;
    node地址&(rt_thread_priority_table[0].next) 减去node的偏移地址,就得到了该node所在的线程结构体初始地址,即存储 *sp  的地址;
3.
    所有类型的指针都是32位的,用来存储地址,关于这里为什么把指针类型强制转换成char*,因为指针的自加加加的是所指向数据类型的地址宽度;
***/

  3.4 线程栈 stack

    线程栈存储该线程的寄存器状态、栈参数;通常是一段内存空间或全局数组;

//main.c    静态分配;  直接定义了线程栈数组;
rt_uint8_t rt_flag1_thread_stack[512];

//thread.c  动态分配;  在rt_thread_create()函数中,通过RT_KERNEL_MALLOC()函数来分配线程栈的动态内存;
stack_start = (void *)RT_KERNEL_MALLOC(stack_size);

    3.4.1 stack_frame 通用寄存器

      线程栈栈底是stack_frame通用寄存器结构体 ,存储内核通用寄存器组的参数;栈底高地址、栈顶低地址;

//cpuport.c
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;
};
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;
};

4 thread 初始化

  对线程的初始化即对线程结构体的初始化;

  本小节两种方式虽然内存分配方式不同,但是均调用_rt_thread_init( );具体见后一节;

  4.1 静态内存分配

    以全局变量的方式来分配线程内存,内存编译器编译的时候就存在,占用空间;

rt_thread_init( )
 //main.c
static rt_uint8_t   led0_blink_entry_stack[RT_MAIN_THREAD_STACK_SIZE];
struct rt_thread    led0_blink_thread;

int main(void)
{	
	rt_thread_init(&led0_blink_thread, 
					"led0_blink",
					led0_blink_entry,
					RT_NULL,
					led0_blink_entry_stack,
					sizeof(led0_blink_entry_stack),
					2,
					50);
}

//thread.c
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);

  4.2 动态内存分配

    以动态分配的方式来分配线程内存,用完可释放,内存利用率高;

rt_thread_create ( )
//thread.c
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;

    //分配rt_object动态内存并初始化;
    thread = (struct rt_thread *)rt_object_allocate(RT_Object_Class_Thread, name);
    if (thread == RT_NULL)
        return RT_NULL;

    //分配stack frame的动态内存;
    stack_start = (void *)RT_KERNEL_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;
}
RTM_EXPORT(rt_thread_create);

5 rt_thread 初始化    

  5.1 rt_object 初始化

    rt_thread 的前一部分即rt_object 对象;首先对其进行初始化;

//object.c
rt_object_t rt_object_allocate(enum rt_object_class_type type, const char *name)
{
    struct rt_object *object;
    register rt_base_t temp;
    struct rt_object_information *information;

    RT_DEBUG_NOT_IN_INTERRUPT;

#ifdef RT_USING_MODULE
    /*
     * get module object information,
     * module object should be managed by kernel object container
     */
    information = (rt_module_self() != RT_NULL && (type != RT_Object_Class_Module)) ?
                  &rt_module_self()->module_object[type] : rt_object_get_information(type);
#else
    //通过对象的数据结构类型,返回容器数组对象;
    information = rt_object_get_information(type);
    RT_ASSERT(information != RT_NULL);
#endif
	//通过容器获取该数据类型的结构体大小,动态分配内存;
    object = (struct rt_object *)RT_KERNEL_MALLOC(information->object_size);
    if (object == RT_NULL)
    {
        /* no memory can be allocated */
        return RT_NULL;
    }

    /* initialize object's parameters */

    //2:数据结构类型
    object->type = type;

    //3:flag标志位
    object->flag = 0;

#ifdef RT_USING_MODULE
    if (rt_module_self() != RT_NULL)
    {
        object->flag |= RT_OBJECT_FLAG_MODULE;
    }
    object->module_id = (void *)rt_module_self();
#endif

    //1:对象字符串
    rt_strncpy(object->name, name, RT_NAME_MAX);

    RT_OBJECT_HOOK_CALL(rt_object_attach_hook, (object));

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

    //4:对象节点
    rt_list_insert_after(&(information->object_list), &(object->list));

    /* unlock interrupt */
    rt_hw_interrupt_enable(temp);

    /* return object */
    return object;
}


//object.c
struct rt_object_information *rt_object_get_information(enum rt_object_class_type type)
{
    int index;
    for (index = 0; index < RT_Object_Info_Unknown; index ++)
        if (rt_object_container[index].type == type) 
			return &rt_object_container[index];
    return RT_NULL;
}


//kservice.c 对象字符串拷贝函数
char *rt_strncpy(char *dst, const char *src, rt_ubase_t n)
{
    if (n != 0){
        char *d = dst;
        const char *s = src;
        do{
            //cae20240104:赋值表达式有返回值,返回值默认为等号右边的赋值变量值;此处为*s的值;
            //当src值为0的时候,当n值未递减完的时候,就往dst填充0;这样的话就避免了搬运ram中的杂乱数据;
            if ((*d++ = *s++) == 0){
                /* NUL pad the remaining n-1 bytes */
                while (--n != 0)
                    *d++ = 0;
                break;
            }
        } while (--n != 0);
    }
    return (dst);
}

  5.2 rt_thread 初始化

    rt_thread在内存分配完毕之后,都是调用 _rt_thread_init( )对rt_thread进行初始化;

//thread.c
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);
    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;

    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;

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

#ifdef RT_USING_MODULE
    thread->module_id = RT_NULL;
#endif

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

    RT_OBJECT_HOOK_CALL(rt_thread_inited_hook, (thread));

    return RT_EOK;
}

    

  5.3 stack_frame 初始化

    在_rt_thread_init( )中初始化rt_thread的时候,会调用rt_hw_stack_init( )对stack_frame进行初始化;

    所以等会BLX回原程序的时候是执行lr中的rt_thread_exit()函数吗?那pc指针里存储的原函数地址又是什么用呢?先放着;

    lr中存放的是当前entry退出的执行函数入口,pc里存放的是当前entry进入的时候执行的函数入口;。。。我怎么会问这么傻屌的问题;

//cpuport.c  传入的stack_addr = &rt_flag1_thread_stack[0] + sizeof(栈) - 4 ;
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; 这里stk指向栈底之外的地址;
    stk  = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stk, 8);                //8字节align,冗余字节dummy;
    stk -= sizeof(struct stack_frame);                                      //这里stk移动到了&r4,返回值;

    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;                        
    stack_frame->exception_stack_frame.r2  = 0;                        
    stack_frame->exception_stack_frame.r3  = 0;                        
    stack_frame->exception_stack_frame.r12 = 0;                        
    stack_frame->exception_stack_frame.lr  = (unsigned long)texit;     /* lr     &rt_thread_exit()地址 */
    stack_frame->exception_stack_frame.pc  = (unsigned long)tentry;    /* entry point, pc  线程函数地址*/
    stack_frame->exception_stack_frame.psr = 0x01000000L;              /* PSR   bit24置1确保返回的是psp*/

    /* return task's current stack address */
    return stk;
}

//地址对齐,冗余地址字节舍弃;
#define RT_ALIGN_DOWN(addr, align)      ((addr) & ~((align) - 1)

  5.4 定时器初始化 

    见定时器章节;

  5.5 rt_thread_exit退出线程

    &rt_thread_exit( ) 的地址已经放入到了lr寄存器中,当线程函数执行完返回的时候就会跳转到rt_thread_exit( )执行;

//thread.c
//if是static rt_object,那么detach rt_object;所以这里并没有对static线程执行什么处理咯?
//else,那么&thread->tlist插入&rt_thread_defunct,等待idle_entry清理;

//当前函数是在线程退出的时候才会执行,所以线程许多都是while(1)不会执行到这里;
void rt_thread_exit(void)
{
    struct rt_thread *thread;
    register rt_base_t level;

    /* get current thread */
    thread = rt_current_thread;

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

    /* remove from schedule */
    rt_schedule_remove_thread(thread);
    /* change stat */
    thread->stat = RT_THREAD_CLOSE;

    /* remove it from timer list */
    rt_timer_detach(&thread->thread_timer);

    if ((rt_object_is_systemobject((rt_object_t)thread) == RT_TRUE) &&
        thread->cleanup == RT_NULL)
    {
        rt_object_detach((rt_object_t)thread);
    }
    else
    {
        /* insert to defunct thread list */
        rt_list_insert_after(&rt_thread_defunct, &(thread->tlist));
    }

    /* enable interrupt */
    rt_hw_interrupt_enable(level);

    /* switch to next task */
    rt_schedule();
}

6  注意事项

//这里的结构体对象和结构体指针都声明成了rt_xx_t的形式,不对称;
/************************************************************************/
struct rt_thread{
    /*...*/
};
typedef struct rt_thread *rt_thread_t;    //声明 rt_thread_t 是结构体指针;

/************************************************************************/
struct rt_list_node{
    struct rt_list_node *next;              
    struct rt_list_node *prev;              
};
typedef struct rt_list_node rt_list_t;    //声明 rt_list_t 是结构体;

/************************************************************************/
struct rt_object{
     /*...*/
};
typedef struct rt_object *rt_object_t;    //声明rt_object_t是结构体指针;

7 小结

  系统是如何从补丁函数启动的;

  单个线程函数是如何初始化的,其中的参数psr pc lr r0具体标识;

  本文总体满意,还算结构清晰;棒棒哒;

posted @ 2023-04-28 18:06  caesura_k  阅读(149)  评论(0编辑  收藏  举报