在RT-Thread实时操作系统中,任务采用了线程来实现,线程是RT-Thread中最基本的调度单位,它描述了一个任务执行的上下文关系,也描述了这个任务所处的优先等级。重要的任务能拥有相对较高的优先级,非重要的任务优先级可以放低,并且可以类似Linux一样具备分时的效果。线程控制块是操作系统用于控制线程的一个数据结构,它会存放线程的一些信息,例如优先级,线程名称等,也包含线程与线程之间连接用的链表结构,线程等待事件集合等。

1、线程控制块(在include/rtdef.h中定义)

  1. /**
  2. * Thread structure
  3. */
  4. struct rt_thread
  5. {
  6. /* rt_object *///这里就是rt_object的结构,其实也可以用rt_object parent来定义,估计线程在早些时候并没有这么做,后来也就没改过来 
  7. char name[RT_NAME_MAX]; /**< the name of thread *///对象的名称
  8. rt_uint8_t type; /**< type of object *///对象类型,这里为RT_Object_Class_Thread
  9. rt_uint8_t flags; /**< thread's flags *///对象标志
  10.  
  11. #ifdef RT_USING_MODULE
  12. void *module_id; /**< id of application module *///线程所在的模块ID
  13. #endif
  14. rt_list_t list; /**< the object list *///内核对象链表节点
  15. rt_list_t tlist; /**< the thread list *///线程链表节点,一般用于相同优先级线程链表中的就绪队列节点
  16.  
  17. /* stack point and entry */
  18. void *sp; /**< stack point *///线程的栈指针,这里主要指静态线程,因为动态线程在堆上分配内存
  19. void *entry; /**< entry *///线程入口函数
  20. void *parameter; /**< parameter *///入口函数对应的参数
  21. void *stack_addr; /**< stack address *///线程栈地址
  22. rt_uint16_t stack_size; /**< stack size *///线程栈大小
  23.  
  24. /* error code */
  25. rt_err_t error; /**< error code */// 线程错误标志,用于IPC机制中,标志是否已经获取成功
  26. rt_uint8_t stat; /**< thread stat *///线程的当前状态,如就绪、挂起等
  27.  
  28. /* priority */
  29. rt_uint8_t current_priority; /**< current priority *///当前优先级  
  30. rt_uint8_t init_priority; /**< initialized priority *///初始优先级  
  31. #if RT_THREAD_PRIORITY_MAX > 32 //在rtconfig.h定义最大线程优先级,默认为32,最大值可为256
  32. rt_uint8_t number; //number, high_mask, number_mask与线程调度时获获取当前最高优先级线程的算法有关,这里对算法不做过多解释
  33. rt_uint8_t high_mask;
  34. #endif
  35. rt_uint32_t number_mask;
  36.  
  37. #if defined(RT_USING_EVENT) //与IPC机制事件相关的一些参数
  38. /* thread event */
  39. rt_uint32_t event_set; //此线程接收到的事件
  40. rt_uint8_t event_info; //此线程的事件过滤信息,用于过滤事件,只保留感兴趣的事件
  41. #endif
  42.  
  43. rt_ubase_t init_tick; /**< thread's initialized tick *///线程时钟节拍数
  44. rt_ubase_t remaining_tick; /**< remaining tick *///线程运行时的剩余时钟节拍数,每隔1个tick,remaining_tick减1。线程初始化时remaining_tick==init_tick
  45.  
  46. struct rt_timer thread_timer; /**< built-in thread timer *///线程定时器
  47.  
  48. void (*cleanup)(struct rt_thread *tid); /**< cleanup function when thread exit *///当线程退出时,需要执行的清理函数,相当于线程的析构函数,用于销毁线程时做些后续操作
  49.  
  50. rt_uint32_t user_data; /**< private user data beyond this thread *///用户数据
  51. };
  52. typedef struct rt_thread *rt_thread_t;

2、线程函数接口(以下函数接口在src/thread.c中实现)

  1. 静态线程初始化:
    静态线程是指,线程控制块、线程运行栈一般都设置为全局变量,在编译时就被确定、被分配处理,内核不负责动态分配内存空间。需要注意的是,用户提供的栈首地址需做系统字节对齐。
  2. rt_err_t rt_thread_init(struct rt_thread *thread, //线程句柄
  3. const char *name, //线程名称,最大长度由rtconfig.h中定义的RT_NAME_MAX宏指定,多余部分会被自动截掉
  4. void (*entry)(void *parameter),//线程入口函数
  5. void *parameter, //线程入口函数参数
  6. void *stack_start, //线程栈起始地址
  7. rt_uint32_t stack_size, //线程栈大小,单位是字节
  8. rt_uint8_t priority, //线程的优先级。优先级范围根据系统配置情况(rtconfig.h中的RT_THREAD_PRIORITY_MAX宏定义)。
  9. rt_uint32_t tick) //线程的时间片大小。时间片(tick)的单位是操作系统的时钟节拍。这个参数只针对相同优先级的线程有效。
    //当系统中存在相同优先级线程时,tick指定线程一次调度能够运行的最大时间长度。这个时间片运行结束时,调度器自动选择下一个就绪态的同优先级线程进行运行。
  10. 动态线程创建:
    调用这个函数时,系统会从动态堆内存中分配一个线程句柄(即TCB,线程控制块)以及按照参数中指定的栈大小从动态堆内存中分配相应的空间。分配出来的栈空间是按照rtconfig.h中配置RT_ALIGN_SIZE方式对齐。
  11. rt_thread_t rt_thread_create(const char *name, //同上
  12. void (*entry)(void *parameter),//同上
  13. void *parameter, //同上
  14. rt_uint32_t stack_size, //同上
  15. rt_uint8_t priority, //同上
  16. rt_uint32_t tick) //同上
  1. 线程脱离:
    rt_thread_detach()函数操作的对象是使用rt_thread_init()函数初始化的静态线程控制块。线程本身不应调用这个接口脱离线程本身。
  2. rt_err_t rt_thread_detach(rt_thread_t thread);
    线程的脱离函数如果当前线程离开后需要进行一些善后工作,即存在cleanup析构函数,此时,会将此线程加入到回收线程链表rt_thread_defunct中去,等到系统空闲时再由空闲线程来“回收"此线程
  3.  
  4. 线程删除:
    在动态线程运行完成或自动结束的情况下,系统会自动删除线程,不需要再调用rt_thread_delete()函数接口。这个接口不应由线程本身来调用以删除线程自身,一般只能由其他线程调用或在定时器超时函数中调用。
  5. rt_err_t rt_thread_delete(rt_thread_t thread);
  6. 调用该函数后,线程对象将会被移出线程队列并且从内核对象管理器中删除,线程占用的堆栈空间也会被释放,收回的空间将重新用于其他的内存分配。
    实际上,用rt_thread_delete函数删除线程接口,仅仅是把相应的线程状态更改为RT_THREAD_CLOSE状态,然后放入到rt_thread_defunct队列中;而真正的删除动作(释放线程控制块和释放线程栈)需要到下一次执行idle线程时,由idle线程完成最后的线程删除动作。
  1. 线程启动:
    创建(初始化)的线程对象的状态处于初始态,并未进入就绪线程的调度队列,我们可以调用下面的函数接口启动一个线程:
  2. rt_err_t rt_thread_startup(rt_thread_t thread);
  3. 当调用这个函数时,将把线程的状态更改为就绪状态,并放到相应优先级队列中等待调度。如果新启动的线程优先级比当前线程优先级高,将立刻切换到这个线程。若没有线程同步机制,系统始终只运行优先级最高的线程。
  4. 线程挂起:
    当线程调用rt_thread_delay时,调用线程将主动使用挂起;当调用rt_sem_takert_mb_recv等函数时,资源(信号量,事件等)不可使用也将导致调用线程挂起。
    处于挂起状态的线程,如果其等待的资源超时(超过其设定的等待时间),那么该线程将不再等待这些资源,并返回到就绪状态;或者当其它线程释放该线程所等待的资源(信号量,事件等)时,该线程也会返回到就绪状态。
  5. rt_err_t rt_thread_suspend(rt_thread_t thread);
  6. 通常不应该使用这个函数来挂起线程本身,如果确实需要采用rt_thread_suspend函数挂起当前任务,需要在调用rt_thread_suspend()函数后立刻调用rt_schedule()函数进行手动的线程上下文切换。
  7.  
  8. 线程恢复:
    线程恢复就是让挂起的线程重新进入就绪状态,如果被恢复线程在所有就绪态线程中,位于最高优先级链表的第一位,那么系统将进行线程上下文的切换。
  9. rt_err_t rt_thread_resume(rt_thread_t thread);
    将线程加入到调度器就绪队列中,并没有真正唤醒它,而真正唤醒线程需要rt_schedule.
  1. 线程让出:
    当前线程的时间片用完或者该线程自动要求让出处理器资源时,它不再占有处理器,调度器会选择下一个最高优先级的线程执行。放弃处理器资源的线程仍然在就绪队列中,只不过放到了同一优先级的就绪线程队列末尾。
  2. rt_err_t rt_thread_yield(void);
  3. 调用该函数后,当前线程首先把自己从它所在的队列中删除,然后把自己挂到与该线程优先级对应的就绪线程链表的尾部,然后激活调度器切换到优先级最高的线程。
    注:rt_thread_yield()函数和rt_schedule()函数比较相像,但在有相同优先级的其他就绪态线程存在时,系统的行为却完全不一样。
    执行rt_thread_yield()后,将当前线程被换出,把相同优先级的下一个就绪线程将被执行。
    执行rt_schedule()后,当前线程并不一定被换出,即使被换出,也不会被放到绪线程链表的尾部,而是在系统中选取就绪的优先级最高的线程执行(如果系统中没有比当前线程优先级更高的就绪线程存在,系统将继续执行当前线程)
  4.  
  5. 线程睡眠:
    rt_err_t rt_thread_sleep(rt_tick_t tick);
    rt_err_t rt_thread_delay(rt_tick_t tick);
    这个函数接受一个参数,该参数指定了线程的休眠时间(单位是OS Tick时钟节拍)。
    这两个函数接口的作用相同,将当前线程挂起后,然后开启线程定时器,加入到定时器超时链表,并等待定时器时间到达,一旦到达,定时器超时回调函数rt_thread_timeout中将会将此线程重新加入到调度器(就绪队列),并重新调度。
    此外,需要特别注意地是,此函数会将超时的线程的error设置为-RT_ETIMEOUT,用来标志此线程并未获得IPC,这在IPC机制中判断某个线程是否已成功获取某个IPC对象时非常有用。
  1. 线程控制:
  2. rt_err_t rt_thread_control(rt_thread_t thread, rt_uint8_t cmd, void *arg);
  3. 指示控制命令cmd当前支持的命令包括
  4. RT_THREAD_CTRL_CHANGE_PRIORITY - 动态更改线程的优先级;
  5. RT_THREAD_CTRL_STARTUP - 开始运行一个线程,等同于rt_thread_startup()函数调用;
  6. RT_THREAD_CTRL_CLOSE - 关闭一个线程,等同于rt_thread_delete()函数调用。
  7. 在修改线程优先级时,当线程处于就绪状态时,为了安全起见,首先将线程从就绪队列中移除,然后再修改优先级,最后再次线程重新加入到调度器的就绪队列中。
  8. 查找线程:
  9. rt_thread_t rt_thread_find(char *name);
  10. 查找线程是通过内核对象管理系统来查找的,根据内核对象的类型,找到相应内核对象链表,并遍历它,比较名字,如果找到则返回。
  11.  
  12. 当前线程:
    在程序的运行过程中,相同的一段代码可能会被多个线程执行,在执行的时候可以通过下面的函数接口获得当前执行的线程句柄
    rt_thread_t rt_thread_self(void);
    请不要在中断服务程序中调用此函数,因为它并不能准确获得当前的执行线程。当调度器未启动时,这个接口返回RT_NULL

3、线程状态

线程运行的过程中,一个时间内只允许一个线程在处理器中运行,从运行的过程上划分,线程有多种不同的运行状态,如运行态,非运行态等。在RT-Thread实时操作系统中,线程包含五种状态,操作系统会自动根据它运行的情况而动态调整它的状态。 RT-Thread中的五种线程状态如下所示:

RT_THREAD_INIT         线程初始状态。   当线程刚开始创建还没开始运行时就处于这个 状态;在这个状态下,线程不参与调度

RT_THREAD_SUSPEND 挂起态、阻塞态。线程此时被挂起:它可能因为资源不可用而 挂起等待;或线程主动延时一段时间而被挂起。在这个状态下 ,线程不参与调度

RT_THREAD_READY     就绪态。           线程正在运行;或当前线程运行完让出处理器后,操 作系统寻找最高优先级的就绪态线程运行

RT_THREAD_RUNNING  运行态。          线程当前正在运行,在单核系统中,只有rt_thread_self()函数返回的线程处于这个状态;在多核系统中则不受这个限制。

RT_THREAD_CLOSE      线程结束态。    当线程运行结束时将处于这个状态。这个状态的 线程不参与线程的调度。

RT-thread内核之线程内核对象的更多相关文章

  1. 【Thread】java线程之对象锁、类锁、线程安全

    说明: 1.个人技术也不咋滴.也没在项目中写过线程,以下全是根据自己的理解写的.所以,仅供参考及希望指出不同的观点. 2.其实想把代码的github贴出来,但还是推荐在初学的您多亲自写一下,就没贴出来 ...

  2. {Python之线程} 一 背景知识 二 线程与进程的关系 三 线程的特点 四 线程的实际应用场景 五 内存中的线程 六 用户级线程和内核级线程(了解) 七 python与线程 八 Threading模块 九 锁 十 信号量 十一 事件Event 十二 条件Condition(了解) 十三 定时器

    Python之线程 线程 本节目录 一 背景知识 二 线程与进程的关系 三 线程的特点 四 线程的实际应用场景 五 内存中的线程 六 用户级线程和内核级线程(了解) 七 python与线程 八 Thr ...

  3. [4]Windows内核情景分析---内核对象

    写过Windows应用程序的朋友都常常听说"内核对象"."句柄"等术语却无从得知他们的内核实现到底是怎样的, 本篇文章就揭开这些技术的神秘面纱. 常见的内核对象 ...

  4. Windows内核之线程简单介绍

    1 线程定义 <1> 内核对象,操作系统用它来对线程实施管理.内核对象也是系统用来存放线程统计信息的地方 <2>还有一个是线程堆栈.它用于维护线程在运行代码时须要的全部函数參数 ...

  5. C#异步编程(三)内核模式线程同步

    其实,在开发过程中,无论是用户模式的同步构造还是内核模式,都应该尽量避免.因为线程同步都会造成阻塞,这就影响了我们的并发量,也影响整个应用的效率.不过有些情况,我们不得不进行线程同步. 内核模式 wi ...

  6. 多线程 用户级线程和内核级线程 from C++多核高级编程

    转 http://book.51cto.com/art/201006/206946.htm 6.1.1 用户级线程和内核级线程 2010-06-21 20:37 齐宁/董泽惠 译 清华大学出版社 字号 ...

  7. Windows内核开发-10-监听对象

    Windows内核开发-10-监听对象 Windows内核除了可以监听进程,线程.dll还可以监听特定的对象和注册表.这里先讲一下监听对象. 监听对象 内核提供了一种可以监听对特定的对象类型的句柄进行 ...

  8. 操作系统学习笔记5 | 用户级线程 && 内核级线程

    在上一部分中,我们了解到操作系统实现多进程图像需要组织.切换.考虑进程之间的影响,组织就是用PCB的队列实现,用到了一些简单的数据结构知识.而本部分重点就是进程之间的切换. 参考资料: 课程:哈工大操 ...

  9. [No00003A]操作系统Operating Systems 内核级线程Kernel Threads内核级线程实现Create KernelThreads

    开始核心级线程 内核级线程对多核的支持怎么样? 和用户级相比,核心级线程有什么不同? ThreadCreate 是系统调用,内核管理TCB ,内核负责切换线程 如何让切换成型? − − 内核栈,TCB ...

随机推荐

  1. Touch table

    On this page I present the results of my touch action research. I concentrated on the few basic acti ...

  2. Java面向对象之抽象方法&接口

    在开始写抽象类之前,有一个问题我觉得想清楚会对理解抽象类很有帮助:那就是为什么要设计抽象类? 难道用类还不够么,为什么要设计出抽象类这样一个东西.我们可以换个角度来理解,就是有些类本来就是不应该被实例 ...

  3. 从PRISM开始学WPF(八)导航Navigation-更新至Prism7.1

    原文:从PRISM开始学WPF(八)导航Navigation-更新至Prism7.1 0x6Navigation [7.1updated] Navigation 在wpf中并没有变化 Basic Na ...

  4. BZOJ2659_算不出的算式_KEY

    题目传送门 其实打表找一找规律可以得出: /************************************************************** Problem: 2659 U ...

  5. [BZOJ3563&3569]DZY Loves Chinese

    bzoj 加强版 sol 其实前一题还有一种解法的,具体方法请见bzoj讨论版. 以下是正解(?) 建一棵生成树. 考虑什么时候图会不连通:当且仅当存在一条树边被删除,同时所有覆盖了他的非树边也被删除 ...

  6. 成都Uber优步司机奖励政策(4月6日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  7. TCP/IP漫游

    TCP/IP漫游 TCP/IP是互联网的基础协议栈,它包括大大小小几十个协议.本篇文章主要涉及到就是HTTP.TCP.IP协议.我们经常学的网络模型是七层或者五层,实际上一般认为一共只有四层就可以了. ...

  8. 一个只有十行的精简MVVM框架

    本文来自网易云社区. 前言 MVVM模式相信做前端的人都不陌生,去网上搜MVVM,会出现一大堆关于MVVM模式的博文,但是这些博文大多都只是用图片和文字来进行抽象的概念讲解,对于刚接触MVVM模式的新 ...

  9. What is the reason that a likelihood function is not a pdf?

    From: http://stats.stackexchange.com/questions/31238/what-is-the-reason-that-a-likelihood-function-i ...

  10. android 学习十四 探索安全性和权限

    1.部署安全性:应用程序必须使用数字证书才能安装到设备上. 2.执行期间的安全性:    2.1 使用独立进程    2.2 使用固定唯一用户ID    2.3  申明性权限模型   3数字证书   ...