RT-Thread学习笔记一:线程基本了解

1.线程:

复杂任务分解为单个独立的可调度的单元,每个单元对应的程序实体为线程。

2.RT-Thread线程调度:

线程调度为抢占式,即从线程就绪列表中找出优先级最高的线程,然后运行。若线程优先级相同,则执行时间片轮转方式调度线程,单位为一个时钟节拍。

创建线程的时候,可以配置线程的时间片参数。时间片仅对优先级相同的就绪线程有效。

时间片的作用是约束线程单次运行的时长,其单位是系统时钟节拍(OS Tick)。

例如:A:10 B:5,则A执行10个节拍,B执行5个节拍。

除了中断处理函数、调度器上锁部分的代码和禁止中断的代码是不可抢占的之外,系统的其他部分都是可以抢占的,包括线程调度器自身。0 优先级代表最高优先级,最低优先级留给空闲线程使用;

3.线程状态分类:

初始状态———就绪状态——运行状态——挂起状态——关闭状态

初始状态,线程刚创建还未开始运行时处于的状态,此状态下,线程不参与调度。
就绪状态,线程具备运行条件的状态,等待被调度器调度执行。
运行状态,线程正在运行。
挂起状态,也称为阻塞状态。由于资源不可用或线程主动延时一段时间,而进入的状态。线程不能执行。
关闭状态,线程运行结束处于的状态。此时线程不参与调度。
 

涉及到的系统调用函数,在后面的学习会进行详细讲解。此处进行简单的说明:

rt_thread_create/init() 创建或初始化一个线程,此线程处于初始状态。

rt_thread_startup() 函数使得初始化状态的线程进入到就绪状态。

rt_thread_delay(),rt_sem_take(), rt_mutex_take() 等函数使得运行状态的线程进入到挂起状态。

rt_thread_resume(), rt_sem_release() 等函数使得挂起状态的线程返回到就绪状态。

rt_thread_delete/detach() 函数将挂起状态的线程更改为关闭状态。

rt_thread_exit(),处于运行状态的线程,运行结束,在线程的最后部分调用此函数,将状态更改为关闭状态。
 

空闲状态:优先级最低,永远为就绪态,不被挂起。 用处:回收被删除线程资源(回收僵尸线程)

rt_thread_yield():当前线程被换出,相同优先级的下一个就绪线程将被执行。

rt_schedule():当前线程并不一定被换出,而是在系统中选取就绪的优先级最高的线程执行。

4.线程的入口函数

入口函数是线程实现预期功能的函数。线程的入口函数由用户设计,一般有以下两种形式:

4.1 无限循环

即在while(1)函数中循环

void thread_entry(void *parameter)
{
	while(1)
	{
	  /* 线程处理 */
	}
}

4.2 有限循环

不会一直循环,执行完毕后被系统删除

5.线程创建:

RT-Thread 提供了先管理相关的系统函数:包含:创建 / 初始化线程、启动线程、运行线程、删除 / 脱离线程 等。

静态创建线程 动态创建线程两种方式

RT-Thread 内核采用面向对象的设计思想进行设计,系统级的基础设施都是一种内核对象,例如线程,信号量,互斥量,定时器等。

内核对象分为两类:静态内核对象和动态内核对象,静态内核对象通常放在RW 段和 ZI 段中,在系统启动后在程序中初始化;动态内核对象则是从内存堆中创建的,而后手工做初始化。
 

线程控制块
在 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;  /* 用户数据 */
};
typedef struct rt_thread *rt_thread_t;

在 RT-Thread 中,要创建一个线程,并使得它能够被执行,需要两步:

  • 创建线程,此时一个新线程被创建,并处于初始状态。
  • 启动线程,此时线程由初始状态进入就绪状态,可以被调度器执行。

5.1 静态创建线程

不用动态分配内存,运行效率高,实时性好。用户分配栈空间和线程句柄。

内存不可释放,只能使用rt_thread_detach() 函数将该线程控制块脱离对象管理器。

static rt_uint8_t thread1_stack[512];   //线程栈
static struct rt_thread thread1;		//线程控制块
rt_thread_init(&thread1,				//线程handle
               "thread1",				//线程名称
               thread1_entry,			//线程入口函数
               RT_NULL,					//线程入口参数
               &thread1_stack[0],       //线程栈地址
               sizeof(thread1_stack),	//线程栈大小
               15, 	 					//线程优先级
               5);			 			//线程时间片
rt_thread_startup(&thread2);			//线程进入就绪态

5.2 动态创建线程

参数简单,内存可以控制释放,用rt_thread_delete() 函数就会将这段申请的内存空间重新释放到内存堆中。

内存大小由系统分配,比静态效率低

static rt_thread_t thread_id = RT_NULL;
thread_id = rt_thread_create("dynamic_th",    //名称
                              dynamic_entry,  //线程代码
                              RT_NULL,        //参数
                              1024,           //栈大小
                              15,             //优先级
                              20);            //时间片
if (thread_id != RT_NULL)
   rt_thread_startup(thread_id);			  //线程进入就绪态
else
   rt_kprintf("dynamic_thread create failure\n");
return RT_EOK;                                                          

线程创建成功,返回线程的控制块指针,也可称为线程句柄。

创建失败,则返回 RT_NULL。

5.3 启动线程

线程创建完成后,需要使其进入就绪状态,也就是启动线程。可以通过调用 rt_thread_startup() 函数来完成。其函数原型为:

rt_err_t rt_thread_startup(rt_thread_t thread)


调用此函数成功后,会将线程放到相应优先级队列中等待调度。如果新启动的线程优先级比当前优先级高,将立即切换到这个线程。

参数 thread ,线程句柄,即线程控制块的指针。

线程启动成功,返回 RT_EOK;启动失败,则返回 -RT_ERROR。
 

6.示例

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#include <rtthread.h>

#define THREAD_PRIORITY    25
#define THREAD_STACK_SIZE  512
#define THREAD_TIMESLICE    5

void thread_entry(void *parameter)
{
	rt_uint32_t count = 0;
	
	while(1)
	{
		/* 线程运行,打印计数 */
		rt_kprintf("thread run: %d\n", count ++);
		rt_thread_mdelay(500);
	}
}

int main(void)
{
	rt_thread_t tid = RT_NULL;
	
    /* 创建线程, 名称是 thread_test, 入口是 thread_entry*/
	tid = rt_thread_create("thread_test",
							thread_entry, RT_NULL,
							THREAD_STACK_SIZE,
							THREAD_PRIORITY, THREAD_TIMESLICE);
							
	/* 线程创建成功,则启动线程 */
	if (tid != RT_NULL)
	{
		rt_thread_startup(tid);
	}

    return 0;
}

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赴遥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值