精华内容
下载资源
问答
  • RT-Thread 中,线程控制块由结构体 struct rt_thread 表示,线程控制块是操作系统用于管理线程的一个数据结构,它存放线程的一些信息,如下面的程序 /* 线程控制块 */ struct rt_thread { /* rt 对象 */ ...

    在 RT-Thread 中,线程控制块由结构体 struct rt_thread 表示,线程控制块是操作系统用于管理线程的一个数据结构,它存放线程的一些信息,如下面的程序

    /* 线程控制块 */
    struct rt_thread
    {
        /* rt 对象 */
        char        name[RT_NAME_MAX];     /* 线程名称 */
        rt_uint8_t  type;                   /* 对象类型 */
        rt_uint8_t  flags;                  /* 标志位 */
    
        rt_list_t   list;                   /* 对象列表 */
        rt_list_t   tlist;                  /* 线程列表 */
    
        /* 栈指针与入口指针 */
        void       *sp;                      /* 栈指针 */
        void       *entry;                   /* 入口函数指针 */
        void       *parameter;              /* 参数 */
        void       *stack_addr;             /* 栈地址指针 */
        rt_uint32_t stack_size;            /* 栈大小 */
    
        /* 错误代码 */
        rt_err_t    error;                  /* 线程错误代码 */
        rt_uint8_t  stat;                   /* 线程状态 */
    
        /* 优先级 */
        rt_uint8_t  current_priority;    /* 当前优先级 */
        rt_uint8_t  init_priority;        /* 初始优先级 */
        rt_uint32_t number_mask;
    
        ......
    
        rt_ubase_t  init_tick;               /* 线程初始化计数值 */
        rt_ubase_t  remaining_tick;         /* 线程剩余计数值 */
    
        struct rt_timer thread_timer;      /* 内置线程定时器 */
    
        void (*cleanup)(struct rt_thread *tid);  /* 线程退出清除函数 */
        rt_uint32_t user_data;                      /* 用户数据 */
    };
    

    其中 init_priority 是线程创建时指定的线程优先级,在线程运行过程当中是不会被改变的(除非用户执行线程控制函数进行手动调整线程优先级)。
    cleanup 会在线程退出时,被空闲线程回调一次以执行用户设置的清理现场等工作。
    user_data 可由用户挂接一些数据信息到线程控制块中,以提供类似线程私有数据的实现。

    展开全文
  • rtthread线程定义与切换实现

    千次阅读 2020-12-17 22:42:47
    title: rtthread线程定义与切换实现 date: 2020-10-22 12:39:15 tags: rtthread 创建线程 包括:线程栈大小、线程栈、线程函数、线程控制块 定义线程栈 在多线程系统中,每个线程都是独立的,互不干扰的,所以要为...

    title: rtthread线程定义与切换实现
    date: 2020-10-22 12:39:15
    tags: rtthread


    创建线程

    包括:线程栈大小、线程栈、线程函数、线程控制块

    定义线程栈

    在多线程系统中,每个线程都是独立的,互不干扰的,所以要为每个线程都分配独立的栈空间,这个栈空间通常是一个预先定义好的全局数组,也可以是动态分配的一段内存空间,但它们都存在于 RAM 中。

    定义线程栈

    线程栈即rt_uint8_t类型数组

    ALIGN(RT_ALIGN_SIZE)
    /* 定义线程栈 */
    rt_uint8_t rt_flag1_thread_stack[512];
    rt_uint8_t rt_flag2_thread_stack[512];
    

    重定义数据类型

    rtdef.h

    /* RT-Thread basic data type definitions */
    typedef signed   char                   rt_int8_t;      /**<  8bit integer type */
    typedef signed   short                  rt_int16_t;     /**< 16bit integer type */
    typedef signed   long                   rt_int32_t;     /**< 32bit integer type */
    typedef unsigned char                   rt_uint8_t;     /**<  8bit unsigned integer type */
    typedef unsigned short                  rt_uint16_t;    /**< 16bit unsigned integer type */
    typedef unsigned long                   rt_uint32_t;    /**< 32bit unsigned integer type */
    typedef int                             rt_bool_t;      /**< boolean type */
    
    /* 32bit CPU */
    typedef long                            rt_base_t;      /**< Nbit CPU related date type */
    typedef unsigned long                   rt_ubase_t;     /**< Nbit unsigned CPU related data type */
    
    typedef rt_base_t                       rt_err_t;       /**< Type for error number */
    typedef rt_uint32_t                     rt_time_t;      /**< Type for time stamp */
    typedef rt_uint32_t                     rt_tick_t;      /**< Type for tick count */
    typedef rt_base_t                       rt_flag_t;      /**< Type for flags */
    typedef rt_ubase_t                      rt_size_t;      /**< Type for size number */
    typedef rt_ubase_t                      rt_dev_t;       /**< Type for device */
    typedef rt_base_t                       rt_off_t;       /**< Type for offset */
    
    /* boolean type definitions */
    #define RT_TRUE                         1               /**< boolean true  */
    #define RT_FALSE                        0               /**< boolean fails */
    
    
    #ifdef __CC_ARM
    		#define rt_inline                   static __inline
    		#define ALIGN(n)                    __attribute__((aligned(n)))
    
    #elif defined (__IAR_SYSTEMS_ICC__)
        #define rt_inline                   static inline
    		#define ALIGN(n)                    PRAGMA(data_alignment=n)
    		
    #elif defined (__GNUC__)
        #define rt_inline                   static __inline
    		#define ALIGN(n)                    __attribute__((aligned(n)))
    #else
        #error not supported tool chain			
    #endif
    
    		
    #define RT_ALIGN(size, align)           (((size) + (align) - 1) & ~((align) - 1))
    #define RT_ALIGN_DOWN(size, align)      ((size) & ~((align) - 1))
    
    		
    #define RT_NULL                         (0)
    

    系统配置文件

    rtconfig.h

    #define RT_THREAD_PRIORITY_MAX  32     /* 最大优先级 */
    #define RT_ALIGN_SIZE           4      /* 多少个字节对齐 */ 
    

    定义线程函数

    线程是一个独立的函数,函数主体无限循环且不能返回。

    /* 线程1 */
    void flag1_thread_entry( void *p_arg )
    {
    	for( ;; )
    	{
    		flag1 = 1;
    		delay( 100 );		
    		flag1 = 0;
    		delay( 100 );
    		
    		/* 线程切换,这里是手动切换 */		
    		rt_schedule();
    	}
    }
    
    /* 线程2 */
    void flag2_thread_entry( void *p_arg )
    {
    	for( ;; )
    	{
    		flag2 = 1;
    		delay( 100 );		
    		flag2 = 0;
    		delay( 100 );
    		
    		/* 线程切换,这里是手动切换 */
    		rt_schedule();
    	}
    }
    

    定义线程控制块

    即结构体指针

    /* 定义线程控制块 */
    struct rt_thread rt_flag1_thread;
    struct rt_thread rt_flag2_thread;
    

    线程控制块类型

    目前线程控制块结构体成员比较少

    rtdef.h

    struct rt_thread
    {
    	void        *sp;	          /* 线程栈指针 */
    	void        *entry;	          /* 线程入口地址 */
    	void        *parameter;	      /* 线程形参 */	
    	void        *stack_addr;      /* 线程起始地址 */
    	rt_uint32_t stack_size;       /* 线程栈大小,单位为字节 */
    	
    	rt_list_t   tlist;            /* 线程链表节点 */
    };
    typedef struct rt_thread *rt_thread_t;
    

    线程链表节点类型

    双向链表

    rtdef.h

    struct rt_list_node
    {
        struct rt_list_node *next;              /* 指向后一个节点 */
        struct rt_list_node *prev;              /* 指向前一个节点 */
    };
    typedef struct rt_list_node rt_list_t;  
    

    链表节点的初始化、插入、删除

    rtservice.h

    		
    /* 初始化链表节点 */
    rt_inline void rt_list_init(rt_list_t *l)
    {
        l->next = l->prev = l;
    }
    
    rt_inline void rt_list_insert_after(rt_list_t *l, rt_list_t *n)
    {
        l->next->prev = n;
        n->next = l->next;
    
        l->next = n;
        n->prev = l;
    }
    
    rt_inline void rt_list_insert_before(rt_list_t *l, rt_list_t *n)
    {
        l->prev->next = n;
        n->prev = l->prev;
    
        l->prev = n;
        n->next = l;
    }
    
    /* 从双向链表删除一个节点 */
    rt_inline void rt_list_remove(rt_list_t *n)
    {
        n->next->prev = n->prev;
        n->prev->next = n->next;
    
        n->next = n->prev = n;
    }
    

    线程创建实现

    初始化线程链表节点,参数赋值给线程控制块,初始化线程栈指针,线程栈指针返回给线程控制块

    thread.c

    rt_err_t rt_thread_init(struct rt_thread *thread,
                            void (*entry)(void *parameter),
                            void             *parameter,
                            void             *stack_start,
                            rt_uint32_t       stack_size)
    {
    	rt_list_init(&(thread->tlist));
    	
    	thread->entry = (void *)entry;
    	thread->parameter = parameter;
    
    	thread->stack_addr = stack_start;
    	thread->stack_size = stack_size;
    	
    	/* 初始化线程栈,并返回线程栈指针 */
    	thread->sp = (void *)rt_hw_stack_init( thread->entry, 
    		                                   thread->parameter,
    							               (void *)((char *)thread->stack_addr + thread->stack_size - 4) );
    	
    	return RT_EOK;
    }
    

    rt_hw_stack_init()函数

    cpuport.c

    /* 线程栈初始化 */
    rt_uint8_t *rt_hw_stack_init(void       *tentry,        //线程入口地址
                                 void       *parameter,     //线程形参
                                 rt_uint8_t *stack_addr)    //线程栈顶地址 - 4
    {
    	
    	
    	struct stack_frame *stack_frame;
    	rt_uint8_t         *stk;
    	unsigned long       i;
    	
    	
    	/* 获取栈顶指针
    	 rt_hw_stack_init 在调用的时候,传给stk的是(栈顶指针)*/
    	stk  = stack_addr + sizeof(rt_uint32_t);
    	
    	/* 让stk指针向下8字节对齐 */
    	stk  = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stk, 8);
    	
    	/* stk指针继续向下移动sizeof(struct stack_frame)个偏移 */
    	stk -= sizeof(struct stack_frame);
    	
    	/* 将stk指针强制转化为stack_frame类型后存到stack_frame */
    	stack_frame = (struct stack_frame *)stk;
    	
    	/* 以stack_frame为起始地址,将栈空间里面的sizeof(struct stack_frame)
    	个内存初始化为0xdeadbeef */
    	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  = 0;                        /* lr */
    	stack_frame->exception_stack_frame.pc  = (unsigned long)tentry;    /* entry point, pc */
    	stack_frame->exception_stack_frame.psr = 0x01000000L;              /* PSR */
    	
    	/* 返回线程栈指针 */
    	return stk;
    }
    

    如果栈顶指针是 8 字节对齐的,在进行向下 8 字节对齐的时候,指针不会移动,如果不是 8 字节对齐的,在做向下 8 字节对齐的时候,就会空出几个字节,不会使用,比如当 stk 是 33,明显不能整除 8,进行向下 8 字节对齐就是 32,那么就会空出一个字节不使用。

    stk 指针继续向下移动 sizeof(struct stack_frame)个偏移,即 16 个字的大小。如果栈顶指针一开始都是 8 字节对齐的

    stk强制转换类型并赋值给指针stack_frame

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qMx3vPUG-1608216139673)(https://i.loli.net/2020/10/22/qGZNb4awLvkrOe3.png)]

    for循环所作的内容如图

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vOMeBIGl-1608216139676)(https://i.loli.net/2020/10/22/6oaEMxS1uTQq3hf.png)]

    最后初始化异常发生时自动保存的寄存器

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pbFuKdhl-1608216139678)(https://i.loli.net/2020/10/22/hYTjU4XK9ogGPap.png)]

    stack_frame结构体

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

    错误码宏定义

    rtdef.h

    /*
    *************************************************************************
    *                               错误码定义
    *************************************************************************
    */
    /* RT-Thread 错误码重定义 */
    #define RT_EOK                          0               /**< There is no error */
    #define RT_ERROR                        1               /**< A generic error happens */
    #define RT_ETIMEOUT                     2               /**< Timed out */
    #define RT_EFULL                        3               /**< The resource is full */
    #define RT_EEMPTY                       4               /**< The resource is empty */
    #define RT_ENOMEM                       5               /**< No memory */
    #define RT_ENOSYS                       6               /**< No system */
    #define RT_EBUSY                        7               /**< Busy */
    #define RT_EIO                          8               /**< IO error */
    #define RT_EINTR                        9               /**< Interrupted system call */
    #define RT_EINVAL                       10              /**< Invalid argument */
    

    实现就绪列表

    线程创建好之后,我们需要把线程添加到就绪列表里面,表示线程已经就绪,系统随时可以调度 。

    就绪列表实际上就是一个rt_list_t类型的数组。

    数组的下标对应了线程的优先级,同一优先级的线程统一插入到就绪列表的同一条链表中。

    schedeler.c

    /* 线程就绪列表 */
    rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX];		// RT_THREAD_PRIORITY_MAX 在rtconfig.h定义
    

    线程初始化

    /* 初始化线程 */
    rt_thread_init( &rt_flag1_thread,                 /* 线程控制块 */
                   flag1_thread_entry,               /* 线程入口地址 */
                   RT_NULL,                          /* 线程形参 */
                   &rt_flag1_thread_stack[0],        /* 线程栈起始地址 */
                   sizeof(rt_flag1_thread_stack) );  /* 线程栈大小,单位为字节 */
    
    /* 初始化线程 */
    rt_thread_init( &rt_flag2_thread,                 /* 线程控制块 */
                   flag2_thread_entry,               /* 线程入口地址 */
                   RT_NULL,                          /* 线程形参 */
                   &rt_flag2_thread_stack[0],        /* 线程栈起始地址 */
                   sizeof(rt_flag2_thread_stack) );  /* 线程栈大小,单位为字节 */
    

    线程插入就绪列表

    /* 将线程插入到就绪列表 */
    rt_list_insert_before( &(rt_thread_priority_table[0]),&(rt_flag1_thread.tlist) );
    rt_list_insert_before( &(rt_thread_priority_table[1]),&(rt_flag2_thread.tlist) );
    

    就绪列表的下标对应的是线程的优先级,但是目前我们的线程还不支持优先级,有关支持多优先级的知识点我们后面会讲到,所以 flag1 和 flag2 线程在插入到就绪列表的时候,可以随便选择插入的位置。

    线程插入就绪列表示意图:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UkeUVkUY-1608216139680)(https://i.loli.net/2020/10/22/g5FNoRJ3DiYCBtu.png)]

    *实现调度器

    调度器是操作系统的核心,其主要功能就是实现线程的切换,即从就绪列表里面找到优先级最高的线程,然后去执行该线程。

    调度器初始化

    schedeler.c

    /* 线程控制块指针,用于指向当前线程 */
    struct rt_thread *rt_current_thread;        // 全局指针,用于指向当前正在运行的线程的线程控制块。
    
    /* 线程休眠列表 */
    rt_list_t rt_thread_defunct;
    
    /* 初始化系统调度器 */
    void rt_system_scheduler_init(void)
    {	
    	/* 用 C 语言关键词 register 修饰,防止被编译器优化。*/
        register rt_base_t offset;	
    
        /* 线程就绪列表初始化 */
        for (offset = 0; offset < RT_THREAD_PRIORITY_MAX; offset ++)
        {
            rt_list_init(&rt_thread_priority_table[offset]);
        }
    
        /* 初始化当前线程控制块指针 */
        rt_current_thread = RT_NULL;
    
        /* 初始化线程休眠列表,当线程创建好没有启动之前会被放入到这个列表 */
        rt_list_init(&rt_thread_defunct);
    }
    

    线程就绪列表初始化后,整个为空,如图

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WVYANNjR-1608216139682)(https://i.loli.net/2020/10/22/AjaRLltUBDFmqpx.png)]

    启动调度器

    调度器在启动的时候会从就绪列表中取出优先级最高的线程的线程控制块,然后切换到该线程。但是目前我们的线程还不支持优先级,那么就手动指定第一个运行的线程为就绪列表下标为 0 这条链表里面挂着的线程。

    /* 启动系统调度器 */
    void rt_system_scheduler_start(void)
    {
        register struct rt_thread *to_thread;
    	
        /* 手动指定第一个运行的线程 */
        to_thread = rt_list_entry(rt_thread_priority_table[0].next,
                                  struct rt_thread,
                                  tlist);
        rt_current_thread = to_thread;
    														
        /* 切换到第一个线程,该函数在context_rvds.S中实现,在rthw.h声明,
            用于实现第一次任务切换。当一个汇编函数在C文件中调用的时候,
            如果有形参,则执行的时候会将形参传人到CPU寄存器r0。*/
        rt_hw_context_switch_to((rt_uint32_t)&to_thread->sp);
    }
    

    rt_list_entry()是一个已知一个结构体里面的成员的地址,反推出该结构体的首地址的宏。

    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表示一个节点的地址(rt_thread_priority_table[0].next),type表示该节点所在的结构体的类型(struct rt_thread),member表示该节点在该结构体中的成员名称(tlist)。

    rt_container_of()的实现算法具体见图:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z5ihTUF6-1608216139685)(https://i.loli.net/2020/10/22/hpuqFfj9yrW7Iez.png)]

    我们知道了一个节点tlist的地址ptr,现在要推算出该节点所在的type类型的结构体的起始地址f_struct_ptr。我们可以将ptr的值减去图中灰色部分的偏移的大小就可以得到f_struct_ptr的地址,现在的关键是如何计算出灰色部分的偏移大小。这里采取的做法是将 0 地址强制类型类型转换为type,即(type *)0,然后通过指针访问结构体成员的方式获取到偏移的大小,即(&((type *)0) -> member)

    最后即可算出f_struct_ptr = ptr - (&((type *)0) -> member)

    第一次线程切换

    rt_hw_context_switch_to()函数

    1. 将形参to赋值给rt_interrupt_to_thread.
    2. rt_interrupt_from_thread赋值为0.
    3. 设置中断标志位rt_thread_switch_interrupt_flag的值为1.
    4. 设置 PendSV 异常的优先级.
    5. 触发 PendSV 异常 (产生上下文切换).

    rt_hw_context_switch_to()函数在rthw.h声明,context_rvds.s实现。

    rthw.h

    void rt_hw_context_switch(rt_uint32_t from, rt_uint32_t to);
    void rt_hw_context_switch_to(rt_uint32_t to);
    

    cpuport.c

    /*
    *************************************************************************
    *                                 全局变量
    *************************************************************************
    */
    rt_uint32_t rt_interrupt_from_thread;            /* 用于存储上一个线程的栈的sp的指针 */
    rt_uint32_t rt_interrupt_to_thread;              /* 用于存储下一个将要运行的线程的栈的sp的指针 */
    rt_uint32_t rt_thread_switch_interrupt_flag;     /* PendSV中断服务函数执行标志 */
    

    当一个汇编函数在C文件中调用的时候,如果有一个形参,则执行的时候会将这个形参传入到CPU寄存器r0,如果有两个形参,第二个则传入到r1

    context_rvds.s

    ;*************************************************************************
    ;                                 全局变量
    ;*************************************************************************
        IMPORT rt_thread_switch_interrupt_flag
        IMPORT rt_interrupt_from_thread
        IMPORT rt_interrupt_to_thread
    		
    ;*************************************************************************
    ;                                 常量
    ;*************************************************************************
    ;-------------------------------------------------------------------------
    ;有关内核外设寄存器定义可参考官方文档:STM32F10xxx Cortex-M3 programming manual
    ;系统控制块外设SCB地址范围:0xE000ED00-0xE000ED3F
    ;-------------------------------------------------------------------------
    SCB_VTOR        EQU     0xE000ED08     ; 向量表偏移寄存器
    NVIC_INT_CTRL   EQU     0xE000ED04     ; 中断控制状态寄存器
    NVIC_SYSPRI2    EQU     0xE000ED20     ; 系统优先级寄存器(2)
    NVIC_PENDSV_PRI EQU     0x00FF0000     ; PendSV 优先级值 (lowest)
    NVIC_PENDSVSET  EQU     0x10000000     ; 触发PendSV exception的值
    	
    ;*************************************************************************
    ;                              代码产生指令
    ;*************************************************************************
    
        AREA |.text|, CODE, READONLY, ALIGN=2
        THUMB
        REQUIRE8
        PRESERVE8
    		
    ;/*
    ; *-----------------------------------------------------------------------
    ; * 函数原型:void rt_hw_context_switch_to(rt_uint32 to);
    ; * r0 --> to
    ; * 该函数用于开启第一次线程切换
    ; *-----------------------------------------------------------------------
    ; */
    		
    rt_hw_context_switch_to    PROC
        
    	; 导出rt_hw_context_switch_to,让其具有全局属性,可以在C文件调用
    	EXPORT rt_hw_context_switch_to
    		
        ; 设置rt_interrupt_to_thread的值
        LDR     r1, =rt_interrupt_to_thread             ;将rt_interrupt_to_thread的地址加载到r1
        STR     r0, [r1]                                ;将r0的值存储到rt_interrupt_to_thread
    
        ; 设置rt_interrupt_from_thread的值为0,表示启动第一次线程切换
        LDR     r1, =rt_interrupt_from_thread           ;将rt_interrupt_from_thread的地址加载到r1
        MOV     r0, #0x0                                ;配置r0等于0
        STR     r0, [r1]                                ;将r0的值存储到rt_interrupt_from_thread
    
        ; 设置中断标志位rt_thread_switch_interrupt_flag的值为1
        LDR     r1, =rt_thread_switch_interrupt_flag    ;将rt_thread_switch_interrupt_flag的地址加载到r1
        MOV     r0, #1                                  ;配置r0等于1
        STR     r0, [r1]                                ;将r0的值存储到rt_thread_switch_interrupt_flag
    
        ; 设置 PendSV 异常的优先级
        LDR     r0, =NVIC_SYSPRI2
        LDR     r1, =NVIC_PENDSV_PRI
        LDR.W   r2, [r0,#0x00]       ; 读
        ORR     r1,r1,r2             ; 改
        STR     r1, [r0]             ; 写
    
        ; 触发 PendSV 异常 (产生上下文切换)
        LDR     r0, =NVIC_INT_CTRL
        LDR     r1, =NVIC_PENDSVSET
        STR     r1, [r0]
    
        ; 开中断
        CPSIE   F
        CPSIE   I
    
        ; 永远不会到达这里
        ENDP
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OuCvTu9G-1608216139686)(https://i.loli.net/2020/10/22/fVQTH5UIYSn2FOK.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-swjLYZC4-1608216139687)(https://i.loli.net/2020/10/22/YbchUyLMkvxNudl.png)]

    **汇编代码产生指令:**当我们新建一个汇编文件写代码时,必须包含类似的指令。AERA 表示汇编一个新的数据段或者代码段,.text 表示段名字,如果段名不是以字母开头,而是以其它符号开头则需要在段名两边加上‘|’,CODE 表示为代码,READONLY 表示只读,ALIGN=2,表示当前文件指令要 22 字节对齐。THUMB 表示THUMB 指令代码,REQUIRE8 和 PRESERVE8 均表示当前文件的栈按照 8 字节对齐。

    PendSV异常: PendSV 异常的优先级为最低。触发 PendSV 异常 (产生上下文切换)。如果前面关了,还要等中断打开才能去执行 PendSV 中断服务函数。

    PendSV_Handler()函数

    1. 失能中断,为了保护上下文切换不被中断
    2. 获取中断标志位,看看是否为0,为0就退出中断.
    3. r1不为0则清0.
    4. 判断rt_interrupt_from_thread的值是否为0,为0表示第一次线程切换
    5. 上文保存(看注释说明),手动保存r4~r11
    6. 下文切换(看注释说明),

    context_rvds.s

    ;/*
    ; *-----------------------------------------------------------------------
    ; * void PendSV_Handler(void);
    ; * r0 --> switch from thread stack
    ; * r1 --> switch to thread stack
    ; * psr, pc, lr, r12, r3, r2, r1, r0 are pushed into [from] stack
    ; *-----------------------------------------------------------------------
    ; */
    
    PendSV_Handler   PROC
        EXPORT PendSV_Handler
    
        ; 失能中断,为了保护上下文切换不被中断
        MRS     r2, PRIMASK
        CPSID   I
    
        ; 获取中断标志位,看看是否为0
        LDR     r0, =rt_thread_switch_interrupt_flag     ; 加载rt_thread_switch_interrupt_flag的地址到r0
        LDR     r1, [r0]                                 ; 加载rt_thread_switch_interrupt_flag的值到r1
        CBZ     r1, pendsv_exit                          ; 判断r1是否为0,为0则跳转到pendsv_exit
    
        ; r1不为0则清0
        MOV     r1, #0x00
        STR     r1, [r0]                                 ; 将r1的值存储到rt_thread_switch_interrupt_flag,即清0
    
        ; 判断rt_interrupt_from_thread的值是否为0
        LDR     r0, =rt_interrupt_from_thread            ; 加载rt_interrupt_from_thread的地址到r0
        LDR     r1, [r0]                                 ; 加载rt_interrupt_from_thread的值到r1
        CBZ     r1, switch_to_thread                     ; 判断r1是否为0,为0则跳转到switch_to_thread
                                                         ; 第一次线程切换时rt_interrupt_from_thread肯定为0,则跳转到switch_to_thread
    
    ; ========================== 上文保存 ==============================
        ; 当进入PendSVC Handler时,上一个线程运行的环境即:
     	; xPSR,PC(线程入口地址),R14,R12,R3,R2,R1,R0(线程的形参)
     	; 这些CPU寄存器的值会自动保存到线程的栈中,剩下的r4~r11需要手动保存
    	
     	
        MRS     r1, psp                                  ; 获取线程栈指针到r1
        STMFD   r1!, {r4 - r11}                          ;将CPU寄存器r4~r11的值存储到r1指向的地址(每操作一次地址将递减一次)
        LDR     r0, [r0]                                 ; 加载r0指向值到r0,即r0=rt_interrupt_from_thread
        STR     r1, [r0]                                 ; 将r1的值存储到r0,即更新线程栈sp
    	
    ; ========================== 下文切换 ==============================
    switch_to_thread
        LDR     r1, =rt_interrupt_to_thread               ; 加载rt_interrupt_to_thread的地址到r1
    	                                                  ; rt_interrupt_to_thread是一个全局变量,里面存的是线程栈指针SP的指针
        LDR     r1, [r1]                                  ; 加载rt_interrupt_to_thread的值到r1,即sp指针的指针
        LDR     r1, [r1]                                  ; 加载rt_interrupt_to_thread的值到r1,即sp
    
        LDMFD   r1!, {r4 - r11}                           ;将线程栈指针r1(操作之前先递减)指向的内容加载到CPU寄存器r4~r11
        MSR     psp, r1                                   ;将线程栈指针更新到PSP
    
    pendsv_exit
        ; 恢复中断
        MSR     PRIMASK, r2
    
        ORR     lr, lr, #0x04                             ; 确保异常返回使用的堆栈指针是PSP,即LR寄存器的位2要为1
        BX      lr                                        ; 异常返回,这个时候任务堆栈中的剩下内容将会自动加载到xPSR,PC(任务入口地址),R14,R12,R3,R2,R1,R0(任务的形参)
    	                                                  ; 同时PSP的值也将更新,即指向任务堆栈的栈顶。在ARMC3中,堆是由高地址向低地址生长的。
        ; PendSV_Handler 子程序结束
    	ENDP	
    
    	ALIGN   4
    
        END
    

    当前文件指令代码要求 4 字节对齐,不然会有警告。

    汇编文件结束,每个汇编文件都需要一个 END。

    系统调度

    系统调度就是在就绪列表中寻找优先级最高的就绪线程,然后去执行该线程。但是目前我们还不支持优先级,仅实现两个线程轮流切换。

    rt_schedule()函数

    /* 系统调度 */
    void rt_schedule(void)
    {
    	struct rt_thread *to_thread;
    	struct rt_thread *from_thread;
    		
    	/* 两个线程轮流切换 */
    	if( rt_current_thread == rt_list_entry( rt_thread_priority_table[0].next,
                                               struct rt_thread,
                                               tlist) )
    	{
    		from_thread = rt_current_thread;
    		to_thread = rt_list_entry( rt_thread_priority_table[1].next,
                                      struct rt_thread,
                                      tlist);
    		rt_current_thread = to_thread;
    	}
    	else
    	{
    		from_thread = rt_current_thread;
    		to_thread = rt_list_entry( rt_thread_priority_table[0].next,
                                      struct rt_thread,
                                      tlist);
    		rt_current_thread = to_thread;																		 
    	}
    	
    	/* 产生上下文切换 */
    	rt_hw_context_switch((rt_uint32_t)&from_thread->sp,(rt_uint32_t)&to_thread->sp);	
    }
    

    如果当前线程为线程 1,则把下一个要运行的线程改为线程 2。

    如果当前线程为线程 2,则把下一个要运行的线程改为线程 1。

    rt_hw_contex_switch()函数

    rt_hw_contex_switch()函数的形参为&thread->sp,即线程栈指针的指针。

    rt_hw_contex_switch()函数用于产生上下文切换,在 context_rvds.S 中实现,在 rthw.h 声明。

    ;/*
    ; *-----------------------------------------------------------------------
    ; * void rt_hw_context_switch(rt_uint32 from, rt_uint32 to);
    ; * r0 --> from
    ; * r1 --> to
    ; *-----------------------------------------------------------------------
    ; */
    	
    rt_hw_context_switch    PROC
        EXPORT rt_hw_context_switch
    
        ; 设置中断标志位rt_thread_switch_interrupt_flag为1     
        LDR     r2, =rt_thread_switch_interrupt_flag          ; 加载rt_thread_switch_interrupt_flag的地址到r2
        LDR     r3, [r2]                                      ; 加载rt_thread_switch_interrupt_flag的值到r3
        CMP     r3, #1                                        ; r3与1比较,相等则执行BEQ指令,否则不执行
        BEQ     _reswitch
        MOV     r3, #1                                        ; 设置r3的值为1
        STR     r3, [r2]                                      ; 将r3的值存储到rt_thread_switch_interrupt_flag,即置1
        
    	; 设置rt_interrupt_from_thread的值
        LDR     r2, =rt_interrupt_from_thread                 ; 加载rt_interrupt_from_thread的地址到r2
        STR     r0, [r2]                                      ; 存储r0的值到rt_interrupt_from_thread,即上一个线程栈指针sp的指针
    
    _reswitch
        ; 设置rt_interrupt_to_thread的值
    	LDR     r2, =rt_interrupt_to_thread                   ; 加载rt_interrupt_from_thread的地址到r2
        STR     r1, [r2]                                      ; 存储r1的值到rt_interrupt_from_thread,即下一个线程栈指针sp的指针
    
        ; 触发PendSV异常,实现上下文切换
    	LDR     r0, =NVIC_INT_CTRL              
        LDR     r1, =NVIC_PENDSVSET
        STR     r1, [r0]
    	
        ; 子程序返回
    	BX      LR
    	
    	; 子程序结束
        ENDP
    

    main函数

    /*
    *************************************************************************
    *                              全局变量
    *************************************************************************
    */
    rt_uint8_t flag1;
    rt_uint8_t flag2;
    
    extern rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX];
    
    /*
    *************************************************************************
    *                      线程控制块 & STACK & 线程声明
    *************************************************************************
    */
    
    /* 定义线程控制块 */
    struct rt_thread rt_flag1_thread;
    struct rt_thread rt_flag2_thread;
    
    ALIGN(RT_ALIGN_SIZE)
    /* 定义线程栈 */
    rt_uint8_t rt_flag1_thread_stack[512];
    rt_uint8_t rt_flag2_thread_stack[512];
    
    /* 线程声明 */
    void flag1_thread_entry(void *p_arg);
    void flag2_thread_entry(void *p_arg);
    
    int main(void)
    {	
    	/* 硬件初始化 */
    	/* 将硬件相关的初始化放在这里,如果是软件仿真则没有相关初始化代码 */	
    	
    	/* 调度器初始化 */
    	rt_system_scheduler_init();
    	
    	
    	/* 初始化线程 */
    	rt_thread_init( &rt_flag1_thread,                 /* 线程控制块 */
    	                flag1_thread_entry,               /* 线程入口地址 */
    	                RT_NULL,                          /* 线程形参 */
    	                &rt_flag1_thread_stack[0],        /* 线程栈起始地址 */
    	                sizeof(rt_flag1_thread_stack) );  /* 线程栈大小,单位为字节 */
    	/* 将线程插入到就绪列表 */
    	rt_list_insert_before( &(rt_thread_priority_table[0]),&(rt_flag1_thread.tlist) );
    	
    	/* 初始化线程 */
    	rt_thread_init( &rt_flag2_thread,                 /* 线程控制块 */
    	                flag2_thread_entry,               /* 线程入口地址 */
    	                RT_NULL,                          /* 线程形参 */
    	                &rt_flag2_thread_stack[0],        /* 线程栈起始地址 */
    	                sizeof(rt_flag2_thread_stack) );  /* 线程栈大小,单位为字节 */
    	/* 将线程插入到就绪列表 */
    	rt_list_insert_before( &(rt_thread_priority_table[1]),&(rt_flag2_thread.tlist) );
    	
    	/* 启动系统调度器 */
    	rt_system_scheduler_start(); 
    }
    
    /*
    *************************************************************************
    *                               函数实现
    *************************************************************************
    */
    /* 软件延时 */
    void delay (uint32_t count)
    {
    	for(; count!=0; count--);
    }
    
    /* 线程1 */
    void flag1_thread_entry( void *p_arg )
    {
    	for( ;; )
    	{
    		flag1 = 1;
    		delay( 100 );		
    		flag1 = 0;
    		delay( 100 );
    		
    		/* 线程切换,这里是手动切换 */		
    		rt_schedule();
    	}
    }
    
    /* 线程2 */
    void flag2_thread_entry( void *p_arg )
    {
    	for( ;; )
    	{
    		flag2 = 1;
    		delay( 100 );		
    		flag2 = 0;
    		delay( 100 );
    		
    		/* 线程切换,这里是手动切换 */
    		rt_schedule();
    	}
    }
    

    以上内容配合野火教程学习。

    展开全文
  • 1. 定义线程的栈 多线程操作系统中,每个线程都是独立的,互不干扰,所以要为每个线程分配独立的栈空间,这个栈空间通常是一个预先定义好的全局...rt_uint8_t rt_flag1_thread_stack[512]; rt_uint8_t rt_flag2_thr...

    1. 定义线程的栈

    多线程操作系统中,每个线程都是独立的,互不干扰,所以要为每个线程分配独立的栈空间,这个栈空间通常是一个预先定义好的全局数组, 也可以是动态分配的一段内存空间,但它们都存在于 RAM 中。定义两个线程栈如下:

    // 定义线程栈 大小设置为512
    
    rt_uint8_t  rt_flag1_thread_stack[512];
    rt_uint8_t  rt_flag2_thread_stack[512];

    2. 定义线程函数

    线程是一个独立的函数,函数主体无限循环不能返回。定义相关线程函数如下:

    /********************************线程函数******************************/
    
    /* 延时函数*/
    
    void delay(uint32_t  count)
    {
       for(;count!=0;count--);
    }
    
    
    /* 线程 1 */
    void flag1_thread_entry(void *p_arg)
    {
      while(1)
        {
          flag1=1;
            delay(100);
            flag1=0;
            delay(100);
            
         /* 线程切换*/
       rt_schedule();        
        }
    }
     
    /* 线程 2 */
    void flag2_thread_entry(void *p_arg)
    {
      while(1)
        {
           flag2=1;
             delay(100);
             flag2=0;
             delay(100);
             /* 线程切换*/
             rt_schedule();    
        }
    }
    
    
    /*******************************************************************************/
    

    3.  定义线程控制块

    如果要顺利调度线程,需要为每个线程额外定义一个线程控制块,存着线程的所有信息(线程栈指针,线程名称,线程形参)。以后系统对线程的全部操作都是通过线程控制块来实现,这里的线程控制块特别重要,一定牢牢掌握。为了使每个线程方便定义线程控制块实体,RT—Thread 在rtdef.h中定义了一个新的数据类型,并将其声明。

    /* 线程控制块声明 */
    struct  rt_thread
    {
          void        *sp;          /* 线程栈指针*/
    	  void        *entry;       /*线程入口地址*/
    	  void        *parameter;   /*线程形参*/
    	  void        *stack_adder; /*线程栈起始地址*/
    	  rt_uint32_t  stack_size;  /*线程栈大小*/
    };
    typedef struct rt_thread  *rt_thread_t;

    在主函数 main 中还需要定义两个 线程控制块,代码如下:

    /* 定义两个线程控制块,对应之前定义的两个线程 */
    
    struct rt_thread  rt_flag1_thread;
    struct rt_thread  rt_flag2_thread;

    4.  线程初始化函数

    前面我们坑此坑次的定义了 线程函数实体,线程的栈,线程控制块,需要将三者联系在一起的就是线程初始化函数,这样才能由系统统一调度。线程初始化函数: rt_thread_init()  改函数在thread.c文件中,代码如下:

    /*  线程出初始化函数 */
    
    rt_err_t   rt_thread_init(struct rt_thread *thread,        
    	                        void (*entry) (void *parameter), 
    							void          *parameter,
    							void          *stack_start,
    							rt_uint32_t    stack_size)
                              
    {
       rt_list_init(&(thread->tlist));
    	 thread->entry =(void*)entry;
    	 thread->parameter =parameter;
    	
    	 thread->stack_adder = stack_start;
    	 thread->stack_size = stack_size;
    	
    	thread->sp =
    	(void*)rt_hw_stack_init(thread->entry,
    		                      thread->parameter,
    	(void*)((char*)thread->stack_adder+thread->stack_size-4));
    	return RT_EOK;
    	
    
    }

    在主函数中建立两个初始化线程,代码如下:

     /* 初始化线程*/
    
    
    	 rt_thread_init(&rt_flag1_thread,                /* 线程控制块*/
    	                flag1_thread_entry ,             /* 线程入口参数*/
    	                RT_NULL,                         /* 线程形参*/
    	                &rt_flag1_thread_stack[0],       /* 线程栈起始地址*/
    	                sizeof(rt_flag1_thread_stack));  /*线程栈大小,单位为字节*/
    	 
    
    	 rt_thread_init(&rt_flag2_thread,                /* 线程控制块*/
    	                flag2_thread_entry ,             /* 线程入口参数*/
    	                RT_NULL,                         /* 线程形参*/
    	                &rt_flag2_thread_stack[0],       /* 线程栈起始地址*/
    	                sizeof(rt_flag2_thread_stack));  /*线程栈大小,单位为字节*/

     

    展开全文
  • RT-Thread 中,线程控制块由结构体 struct rt_thread 表示,线程控制块是操作系统用于管理线程的一个数据结构,它会存放线程的一些信息,例如优先级、线程名称、线程状态等,也包含线程与线程之间连接用的链表...

    线程的工作机制

    在 RT-Thread 中,线程控制块由结构体 struct rt_thread 表示,线程控制块是操作系统用于管理线程的一个数据结构,它会存放线程的一些信息,例如优先级、线程名称、线程状态等,也包含线程与线程之间连接用的链表结构,线程等待事件集合等,详细定义如下:

    /* 线程控制块 */
    struct rt_thread
    {
        /* rt 对象 */
        char        name[RT_NAME_MAX];     /* 线程名称 */
        rt_uint8_t  type;                   /* 对象类型 */
        rt_uint8_t  flags;                  /* 标志位 */
    
        rt_list_t   list;                   /* 对象列表 */
        rt_list_t   tlist;                  /* 线程列表 */
    
        /* 栈指针与入口指针 */
        void       *sp;                      /* 栈指针 */
        void       *entry;                   /* 入口函数指针 */
        void       *parameter;              /* 参数 */
        void       *stack_addr;             /* 栈地址指针 */
        rt_uint32_t stack_size;            /* 栈大小 */
    
        /* 错误代码 */
        rt_err_t    error;                  /* 线程错误代码 */
        rt_uint8_t  stat;                   /* 线程状态 */
    
        /* 优先级 */
        rt_uint8_t  current_priority;    /* 当前优先级 */
        rt_uint8_t  init_priority;        /* 初始优先级 */
        rt_uint32_t number_mask;
    
        ......
    
        rt_ubase_t  init_tick;               /* 线程初始化计数值 */
        rt_ubase_t  remaining_tick;         /* 线程剩余计数值 */
    
        struct rt_timer thread_timer;      /* 内置线程定时器 */
    
        void (*cleanup)(struct rt_thread *tid);  /* 线程退出清除函数 */
        rt_uint32_t user_data;                      /* 用户数据 */
    };
    

    代码如下:

    #include <rtthread.h>
    #define THREAD_PRIORITY 25
    #define THREAD_STACK_SIZE 512
    #define THREAD_TIMESLICE 5
    
    static rt_thread_t tid1 = RT_NULL, tid2 = RT_NULL;
    
    /** 线程 1 采用低优先级运行,一直打印计数值 */ 
    
    static void thread1_entry(void *parameter){
        rt_uint32_t count = 0;
        rt_kprintf("thread1 create! \n");
        while(1){
            rt_kprintf("thread1 count: %d\n",count++);
            count++;
        }
    
    }
    
    
    static void thread1_cleanup(struct rt_thread *tid){
        if(tid != tid1)
        {
            return;
        }
        rt_kprintf("thread1 end \n");
        tid1 = RT_NULL;
    }
    /*  线程 2 唤醒后直接删除线程 1,删除线程 1 后,线程 1 自动脱离就绪线程 */
    static void thread2_entry(void *parameter){
        rt_kprintf("thread2 create entry!\n");
         rt_uint32_t count = 0;
        
        rt_thread_delay(10);
        rt_thread_delete(tid1);
        
        rt_thread_delay(10);
    }
    
    static void thread2_cleanup(struct rt_thread *tid)
    {
        if(tid != tid2)
        {
            return ;
        }
        rt_kprintf("thread2 end\n");
        tid2 = RT_NULL;
    }
    
    int thread_sample_init(){
        rt_thread_t init_thread;
    
        rt_err_t result;
    
        tid1 = rt_thread_create("t1",
        thread1_entry,
        RT_NULL,
        THREAD_STACK_SIZE,
        THREAD_PRIORITY,
        THREAD_TIMESLICE);
    
        if(tid1 != RT_NULL)
        {
            tid1->cleanup = thread1_cleanup;
            rt_thread_startup(tid1);
        }
        tid2 = rt_thread_create("t2",
        thread2_entry,
        RT_NULL,
        THREAD_STACK_SIZE,
        THREAD_PRIORITY - 1,
        THREAD_TIMESLICE);
    
        if(tid2 != RT_NULL)
        {
            tid2->cleanup = thread2_cleanup;
            rt_thread_startup(tid2);
        }
        return 0;
    }
    
    MSH_CMD_EXPORT(thread_sample_init, run signal sample);
    

    运行效果如下:
    在这里插入图片描述

    展开全文
  • RT_Thread 线程学习笔记

    2021-01-23 12:04:28
    (一)线程控制块: (1)、线程创建: rt_thread_t rt_thread_create(const char *name, void (*entry)(void *parameter), void *parameter, rt_uint32_t stack_size,
  • rt-thread线程源码分析

    千次阅读 2013-02-17 14:10:16
    rt-thread操作系统是一个...1 线程控制块的数据结构 /** * Thread structure */ struct rt_thread { /* rt object *///这里就是rt_object的结构,其实也可以用rt_object parent来定义,估计线程在早些时候并没有这么
  • RT-Thread 线程调度

    2016-05-17 09:32:00
    /* 变量分配4字节对齐 */ ALIGN(RT_ALIGN_SIZE) /* 静态线程的 线程堆栈*/ static rt_uint8_t thread1_stack[512];.../* 静态线程的 线程控制块 */ static struct rt_thread thread_test1; ...
  • 1.静态创建线程--rt_thread_init 步骤: 1)必须先定义: ...只能使用 rt_thread_detach函数将该线程控制块从对象管理器中脱离 void rosserial_thread_init(void) { rt_thread_init(&rosserial_thread,
  • RT-Thread中是用线程控制块来描述线程实体的,在 RT-Thread 中,线程控制块由结构体 struct rt_thread 表示,线程控制块是操作系统用于管理线程 的一个数据结构,它会存放线程的一些信息,例如优先级、线程名称、...
  • RT-Thread-线程的创建

    2021-03-16 14:53:59
    RT-Thread中,线程由三部分组成:线程代码(入口函数)、线程控制块、线程堆栈 三 线程创建 1、创建进程 (1)创建静态线程 rt_err_t rt_thread_init(struct rt_thread *thread, ...
  • 使用静态定义方式时,必须先定义静态的线程控制块,并且定义好堆栈空间,然后调用rt_thread_init来完成线程的初始化工作。采用这种方式,线程控制块和堆栈占用的内存会放在RW段(一个ARM程序包含3部分:RO,RW和ZI,...
  • 众所周知RT-Thread 操作系统的线程当中,一共有两种--静态线程和是动态线程,而RT-Thread 中的线程一般由三部分组成:线程代码(函数)、 线程控制块、 线程堆栈 那线程的创建又是怎么实现的呢? 首先咱们先看一下...
  • 目的 1 了解除了全局变量之外更加安全的通信方式 正文 1 邮箱 特点:开销低,效率高,非堵塞,可用于中断 一个邮件大小是 4 个字节针对 32 bit处理系统,用于...邮箱控制块是操作系统用于管理邮箱的数据结构 rt_...
  • /* 静态线程的 线程堆栈*/ static rt_uint8_t thread1_stack[512]; static rt_uint8_t .../* 静态线程的 线程控制块 */ static struct rt_thread thread_test1; static struct rt_thread thread_test2; ...
  • RT-Thread创建静态、动态线程

    千次阅读 2016-07-13 21:15:48
    RT-Thread 实时操作系统核心是一个高效的硬实时核心,... RT-Thread 中的“线程”一般由三部分组成:线程代码(函数)、 线程控制块、 线程堆栈。  /* 指向线程控制块的指针*/ static struct rt_thread led
  • 看视频笔记如下。。。。 在RT-Thread中,与上述小任务对应的...RT_Thread中,线程有三部分组成:线程代码(入口函数),线程控制块,线程堆栈。 线程代码如下: 1.无限循环模式 void thread_entry(void *parameter..
  • 功能 RT-Thread 线程管理的主要功能是对线程进行管理和调度。 系统中总共存在两类线程,一类是系统...每个线程都有重要的属性,如线程控制块、线程栈、入口函数和线程状态 线程优先级,时间片,线程入口函数等。...
  • 线程是实现任务的载体,它是 RT-Thread中最基本的调度单位,它描述了一个...RT-Thread中,线程由三部分组成,线程代码(入口函数)、线程控制块、线程堆栈 时间片轮转调度 优先级和时间片是线程的两个重要参...
  • 目录 线程控制块 线程的重要属性 线程的状态转换 系统线程
  • 线程管理之线程基础前言基本信息前言说明线程的工作机制线程控制块线程重要属性线程栈线程状态线程优先级时间片线程的入口函数无限循环模式:顺序执行或有限次循环模式:线程错误码线程状态切换系统线程空闲线程主线...
  • RT-Thread实时操作系统中,任务采用了线程来实现,线程是RT-Thread中最基本...线程控制块是操作系统用于控制线程的一个数据结构,它会存放线程的一些信息,例如优先级,线程名称等,也包含线程与线程之间连接用的链表
  • 线程控制块 线程栈 线程代码(入口函数) 可以是无限循环结构或者顺序执行。 void thread1_entry(void* parameter) { while(1) { ... } } void thread2_entry(void* parameter) { //事务处理1 //事务处理2 ...
  • 定义线程控制块 实现线程创建函数 定义线程栈 ALIGN(RT_ALIGN_SIZE) //定义线程栈 rt_uint8_t rt_flag1_thread_stack[512]; rt_uint8_t rt_flag2_thread_stack[512]; 定义线程函数 (线程独立,无线循环,...
  • RT-Thread操作系统之一线程总结

    千次阅读 2014-05-26 21:12:56
    组成 --------线程代码(函数),线程控制块、线程堆栈。   ·线程代码 /* static void thread1_entry(viod* parameter) {   }*/ ·线程控制块 --主要就是初始化rt_thread结构体,它用来记录线程的...

空空如也

空空如也

1 2 3 4 5
收藏数 94
精华内容 37
关键字:

rtthread线程控制块