RT-Thread系统关于创建线程1

  1. 定义线程栈
    在多线程系统中,每个线程都要分配独立的栈空间,每个栈空间通常是一个预定义好的全局数组,也可以是动态分配的一段内存空间,都是存在于RAM中,也就是所谓的栈中(RAM中一段连续的内存空间)。
    在多线程系统中,有多少个线程就需要定义多少个线程栈。
ALIGN(RT_ALIGN_SIZE)//    (2)
/* 定义线程栈*/
rt_uint8_t rt_flag1_thread_stack[512];//   (1)
rt_uint8_t rt_flag2_thread_stack[512];

线程的定义:一个预定义好的全局数据,数据类型是rt_uint8_t,大小设为512。在RT-Thread都会将标准的C数据类型用typedef重新取一个类型名,以“rt”前缀开头,这些重定义的数据类型都放在rtdef.h中。
其中ALIGN是一个 带参宏,用来设置变量需要多少字节进行对齐。RT_ALIGN_SIZE是在rtconfig.h中定义的,默认是4,表示4个字节对齐。
2. 定义线程函数
线程是一个独立的函数,函数内部无限循环且不能返回。

/* 线程1 */
void flag1_thread_entry( void *p_arg )//   (1)
{
    for( ;; )
    {
        flag1 = 1;
        delay( 100 );
        flag1 = 0;
        delay( 100 );

        /* 线程切换,这里是手动切换 */
        rt_schedule();
    }
}

  1. 定义线程控制块
    在多线程系统中,线程的执行是由系统调度的,系统为了顺利调度系统,为每个线程都额外定义了一个线程控制块,这个线程控制块包含线程左右的信息,如线程的栈指针,线程名称,线程的形参等,类似于线程的ID身份证。
struct rt_thread//    (1)
{
    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;//    (2)

在RT-Thread中都会给新声明的数据结构重新定义一个指针,使用struct rt_thread xxx这种形式进行定义,使用时用rt_thread_t xxx的形式。

/* 定义线程控制块 */
struct rt_thread rt_flag1_thread;
struct rt_thread rt_flag2_thread;
  1. 实现线程创建函数
    线程的栈、线程的函数实体、线程的控制块这三者联系起来才能由系统进行统一的调度。这个联系工作就需要线程初始化函数rt_thread_init()函数来实现,该函数在thread.c中定义,在rtthread.h中进行声明。
rt_err_t rt_thread_init(struct rt_thread *thread,//          (1)
                        void (*entry)(void *parameter),//    (2)
                        void             *parameter,//       (3)
                        void             *stack_start,//     (4)
                        rt_uint32_t       stack_size)//      (5)
{
    rt_list_init(&(thread->tlist));//                         (6)

    thread->entry = (void *)entry;//                       (7)
    thread->parameter = parameter;//                       (8)

    thread->stack_addr = stack_start;//                    (9)
    thread->stack_size = stack_size;//                     (10)

    /* 初始化线程栈,并返回线程栈指针 */ //                      (11)
    thread->sp = (void *)rt_hw_stack_init( thread->entry,
                                        thread->parameter,
                                    (void *)((char *)thread->stack_addr + thread->stack_size - 4) );

    return RT_EOK;//                                          (12)
}

以小写rt开头,表示这是一个外部函数,可以由用户调用,以_rt开头的函数表示内部函数,只能由RT-Thread内部使用。
(1) :thread是线程控制块指针。
(2) :entry 是线程函数名, 表示线程的入口。
(3) :parameter是线程形参,用于传递线程参数。
(4) :stack_start 用于指向线程栈的起始地址。
(5) :stack_size表示线程栈的大小,单位为字节。
(6) :初始化线程链表节点,往后我们要把线程插入到各种链表中,就是通过这个节点来实现的,它就好像是线程控制块里面的一个钩子,可以把线程控制块挂在各种链表中。
(7) :将线程入口保存到线程控制块的entry成员中。
(8) :将线程入口形参保存到线程控制块的parameter成员中。
(9) :将线程栈起始地址保存到线程控制块的stack_start成员中。
(10) :将线程栈起大小保存到线程控制块的stack_size成员中。
(11) :初始化线程栈,并返回线程栈顶指针。 rt_hw_stack_init()用来初始化线程栈
5. 实现链表相关函数
rt_list_init(&(thread->tlist));
在初始化之前我们需要在线程控制块中添加一个线程链表节点,如下所示

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

   rt_list_t   tlist;            /* 线程链表节点 *///   (1)
};
typedef struct rt_thread *rt_thread_t;

(1)这语句就是在线程控制块中添加的一个线程链表节点,线程链表节点tlist的数据类型是是 rt_list_t,该数据类型在rtdef.h中定义

  • 定义链表节点数据类型, rt_list_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类型的节点里面有两个 rt_list_t类型的节点指针next和prev,分别用来指向链表中的下一个节点和上一个节点。
在这里插入图片描述

  • 初始化链表节点
    链表的相关操作函数都在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; /* 第 1 步*/
    n->next = l->next; /* 第 2 步*/
    l->next = n; /* 第 3 步*/
    n->prev = l; /* 第 4 步*/
}

在这里插入图片描述
l指的是第一个节点,n指新插入的节点。

l->next->prev = n; /* 第 1 步*/就是在l第一个节点后面插入新的节点n;
相当于把新的节点n赋值给l节点。
n->next = l->next; /* 第 2 步*/把新节点向后指的指针next指向第一个节点的向后指的指针next。由于原来l->next指向的是NODE2,所以现在再把n->next指向原来的NODE2,也就是现在图中的NODE3。
l->next = n; /* 第 3 步*/把第一个节点向后指的指针next指向新节点n。原来l->next指向的是NODE2,现在要指向新插入的节点n,也就是图中的NewNode。上面这两部就把新节点的next指针已经连续起来了
n->prev = l; /* 第 4 步*/把新节点向前指的指针指向第一个节点。把新节点的n->prev指向l,也就是替代原来NODE2的prev指向l。这就把新插入节点n的prev也连接起来了。

tips:p->next->n含义
p是一个指向结构体或共用体的指针,p->next就是取得这个结构体或共用体next成员,而next又是一个结构体或共用体指针,这里面有一个成员n. p->next->n就是访问这个n变量.
如果都是结构体, 大概是这样子的:

struct
{
		struct
		{
			char n;
			...
		}*next;
		...
}*p;
  • 在双向链表表头前面插入一个节点:
    主要分为以下四步:
rt_inline void rt_list_insert_before(rt_list_t *l, rt_list_t *n)
{
    l->prev->next = n; /* 第 1 步*/
    n->prev = l->prev; /* 第 2 步*/
    l->prev = n; /* 第 3 步*/
    n->next = l; /* 第 4 步*/
}

在这里插入图片描述
l->prev->next = n; /* 第 1 步*/把新插入的节点赋值给第一个节点l,相当于插入一个新的节点
n->prev = l->prev; /* 第 2 步*/把新节点n的prev向前指向的指针赋值为第一个节点l的prev,原先第一个节点l的prev是指向最后
l->prev = n; /* 第 3 步*/把第一个节点l的prev向前指向的指针赋值为新加节点n,这样就把prev连接起来了
n->next = l; /* 第 4 步*/新家节点n的next指向第一个节点l,这样就把next连接起来了

  • 从双向链表删除一个节点:
    主要分为以下3步:
rt_inline void rt_list_remove(rt_list_t *n)
{
   n->next->prev = n->prev; /* 第 1 步*/
   n->prev->next = n->next; /* 第 2 步*/
   n->next = n->prev = n; /* 第 3 步*/
}

在这里插入图片描述
n->next->prev = n->prev; /* 第 1 步*/
n->prev->next = n->next; /* 第 2 步*/
n->next = n->prev = n; /* 第 3 步*/

未完待续。。。。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值