当前位置: 首页 > 工具软件 > RT-Thread > 使用案例 >

rt-thread 线程知识

张高澹
2023-12-01

线程是操作系统的一个重要概念,正是因为有了线程,操作系统才可以实现多任务。在了解线程之前,先来看一下在没有操作系统之前实现多任务是怎样的。
所谓的多任务就是让MCU能够在宏观上同时运行多个函数,在没有使用操作系统的时候,实现多任务通常是利用定时器判断标志位,然后在主函数中根据标志位来选择不同的函数执行。但是这种方法的实时性很差,并且不能自由地控制函数的执行,停止。

int flag = 0;
void main(void)
{
	while(1)
	{	
		switch(flag)
		{
			case 1:
				task1();
			break;
			case 2:
				task2();
			break;
			case 3:
				task3();
			break;
		}
	}

}

void timer_handler(void)
{
	static int count = 0;
	count++;
	if(count <= 50)
	{
		flag = 1;
	}
	else if(count > 50 && count <= 100)
	{
		flag = 2;
	}
	else if(count > 100 && count <= 150)
	{
		flag = 3;
	}
	else
	{
		count = 0;
	}

}

在rt-thread中利用线程来实现多任务功能,线程是rt-thread操作系统中的一个很重要的概念,在rt-thread中线程有有两种,分别是系统线程和用户线程,系统线程由rt-thread操作系统创建,主要用于管理内核的一些资源。而用户线程由用户自己创建。

  • 线程控制块
    rt-thread为了更好的对线程进行控制,定义了一个叫线程控制块的数据结构,把线程的所有属性都包含在里面,线程控制块中的一些重要参数都已经做了注释。
struct rt_thread
{
    /* rt object */
    char        name[RT_NAME_MAX];                     //线程名字
    rt_uint8_t  type;                                   //对象类型
    rt_uint8_t  flags;                                  //标志位

#ifdef RT_USING_MODULE
    void       *module_id;                              /**< id of application module */
#endif

    rt_list_t   list;                                   //对象链表
    rt_list_t   tlist;                                 //线程链表

    /* stack point and entry */
    void       *sp;                                    //栈指针
    void       *entry;                                  //入口函数
    void       *parameter;                             //线程参数
    void       *stack_addr;                             //栈起始地址
    rt_uint32_t stack_size;                             //栈大小

    /* error code */
    rt_err_t    error;                                  //错误码

    rt_uint8_t  stat;                                   //线程状态

#ifdef RT_USING_SMP
    rt_uint8_t  bind_cpu;                               /**< thread is bind to cpu */
    rt_uint8_t  oncpu;                                  /**< process on cpu` */

    rt_uint16_t scheduler_lock_nest;                    /**< scheduler lock count */
    rt_uint16_t cpus_lock_nest;                         /**< cpus lock count */
    rt_uint16_t critical_lock_nest;                     /**< critical lock count */
#endif /*RT_USING_SMP*/

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

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

    /* light weight process if present */
#ifdef RT_USING_LWP
    void        *lwp;
#endif

    rt_uint32_t user_data;                             //用户私有数据
};
  • 线程优先级
    rt-thread中可以设置线程优先级,优先级高的线程可以抢占优先级低的线程。rt-thread中支持的优先级为256级(0-255),其中优先级数字越小,优先级越高。不同的MCU可以根据实际情况修改最大优先级,默认最低的优先级给空闲进程使用,用户可以根据线程的功能自行选择优先级。
  • 线程状态
    rt-thread中线程拥有不同的状态,有运行态,睡眠态,就绪态等
    初始态:线程创建完成,但是并没有运行,不参与调度,此时线程状态为初始态RT_THREAD_INIT。
    就绪态:线程被放到就绪队列中等待调度器调度,此时线程状态为RT_THREAD_READY。
    运行态:线程获得CPU和外部资源并开始运行,此时线程状态为运行态RT_THREAD_RUNNING。
    挂起态:挂起态也叫阻塞态,线程在等待某些资源,比如信号量,互斥量,事件,消息等,也可以是线程调用睡眠延时函数,此时线程状态为挂起态RT_THREAD_SUSPEND。
    关闭态:线程退出运行,不参与调度,此时线程为关闭态RT_THREAD_CLOSE。
  • 线程时间片

在实时操作系统中,每个线程都有自己的时间片,所谓时间片就是线程一次可以运行多长事件。时间片仅对优先级相同的线程有用,当线程优先级相同时,会采用时间片轮转调度的方式进行线程的调度。每个线程都安装规定的时间片运行,当时间片到的时候,线程就会让出CPU,给下一个进程运行,如此循环所有优先级相同的进程。

  • 线程入口函数
    在创建线程时都会有一个线程入口函数,这个函数就是用户自定义的函数。
void thread_entry(void* paramenter)
{
	while (1)
	{
		/* 等 待 事 件 的 发 生 */
		/* 对 事 件 进 行 服 务、 进 行 处 理 */
	}
}

通过会在线程函数中循环做一些事情,但是这里需要注意一点是,不要让线程函数陷入死循环。在优先级相同的线程中,可以让线程陷入死循环,因为优先级相同会采用时间片调度的方法依次运行。但是当高优先级线程陷入死循环时,会导致低优先级的线程无法执行。所以在线程中要有让出CPU的行为。

  • 线程错误码
    在线程的生命周期中,可能会出现各种的意想不到的错误,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 */
  • 线程栈
    每个线程都有自己独立的线程栈,线程栈在线程创建的时候被分配。当线程需要被切换的时候,会将线程的一些信息保存在线程栈中,当线程恢复的时候会从线程栈中恢复线程信息,同时线程栈也用来保存线程定义的局部变量。
 类似资料: