Zephyr OS nano 内核篇: 等待队列 wait_q
来源:互联网 发布:掘金比赛数据 编辑:程序博客网 时间:2024/05/15 23:51
Zephyr OS 所有的学习笔记已托管到 Github,CSDN 博客里的内容只是 Github 里内容的拷贝,因此链接会有错误,请谅解。
最新的学习笔记请移步 GitHub:https://github.com/tidyjiang8/zephyr-inside
本文先描述 Zephyr OS 中线程的状态关系,然后分析线程的等待队列的相关操作。
Zephyr OS 中的等待队列相关的代码很少,仅有几十行,但是理解这些代码却不是那么轻松,因此为了便于大家理解,画了若干图。
线程的状态切换
Zephyr OS 中线程的状态可以分为三类:
- 执行态:指当前正在占用 CPU 执行任务的线程。由于物联网设备的低成本、低速率的特性,Zephyr OS 只支持单 CPU,因此内核中只有一个线程处于执行态。
- 就绪态:指已经准备就绪、等待被调度执行的线程,这些线程以优先级的顺序排列。当系统当前正在执行的线程执行完毕或进入等待队列时,调度算法会调度就绪态链表中优先级最高的线程。
- 等待态:当内核中的一个线程正在执行时,可能由于某种原因无法再执行下去(比如等待某种资源——信号量),这时我们可以将该线程放入一个等待队列中,然后将 CPU 的执行权转交给其它线程,以达到最大效率使用 CPU 的目的。
线程的状态切换如图 1 所示。
等待队列的应用场景
Zephyr OS 中,以下几种场景将会使用等待队列:
- 信号量:当多个线程都需要使用信号量时,可能信号量被别的线程用尽(信号量减至0),另一个线程再去获取该信号量时,得不到信号量,该线程就将自己加入到等待队列中,并释放 CPU。
- fifo:fifo 是先进先出的队列,使用的场景是这样的:某个线程(生产者)向 fifo 中写数据,另一个线程(消费者)从 fifo 中读取数据。当读线程(消费者)从 fifo 中取数据时是空的,该线程就将自己加入到等待队列中,并释放 CPU。
- lifo:lifo 是后进先出的队列,使用的场景与 fifo 类似。
- timeout:超时服务是指线程在指定的超时时间后再运行。线程在使用超时服务时,会将自己加入到等待队列中。当超时时间到期时,调度器会将该线程从等待队列中取出。
以上内容都是推测出来的,其正确与否会在今后的学习过程中得到验证。
等待队列的定义
struct _nano_queue { void *head; void *tail;};
该结构体包含两个成员:
- head:用于指向等待队列的队首线程。当队列为空时,指向NULL。
- tail:用于指向等待队列的队尾线程。当队列位空时,指向该队列本身。
对于如下程序:
struct _nano_queue wait_q;
其在内存空间存储情况如图 2 所示。
等待队列的初始化
static inline void _nano_wait_q_reset(struct _nano_queue *wait_q){ wait_q->head = (void *)0; wait_q->tail = (void *)&(wait_q->head);}static inline void _nano_wait_q_init(struct _nano_queue *wait_q){ _nano_wait_q_reset(wait_q);}
进行队列结构体中成员的初始化:
- 将 head 成员指向 NULL。
- 将 tail 成员指向该队列自身(参考后面的图2)。
_nano_wait_q_init() 和 _nano_wait_q_reset() 是两个完全等价的函数。
对于如下程序:
struct _nano_queue wait_q;_nano_wait_q_init(&wait_q);
其在内存空间的存储情况如图 3 所示。
在队尾插入线程
static inline void _nano_wait_q_put(struct _nano_queue *wait_q){ ((struct tcs *)wait_q->tail)->link = _nanokernel.current; wait_q->tail = _nanokernel.current;}
很简单的两句话,但是理解起来蛮吃力的。我们分两种情况考虑:
当等待队列为空时插入线程
先回忆一下描述线程的结构体 struct cts 的定义:
struct tcs { struct tcs *link; uint32_t flags; uint32_t basepri; int prio; ...};
其在内存空间的存储情况如图 4 右边部分所示。我们需要注意其 link 成员,它是 struct tcs 的第一个成员,其类型是一个指向线程的指针。
当队列为空时,wait_q 的 tail 成员指向该队列自身的 head 成员,其结构如图 3 所示。((struct tcs )wait_q->tail) 将 tail 强制转换为一个线程结构体指针,也就是说,tail 所指向的地址用于存放一个线程,而由于 link 是线程结构体 struct tcs 的第一个成员,且是一个指针,所以 ((struct tcs )wait_q->tail)->link 的含义就是有一个指针变量,该变量自身的地址等于 &wait_q->tail(即&wait_q->head), 该变量指向了一个线程的结构体。
所以,((struct tcs *)wait_q->tail)->link = _nanokernel.current; 的含义就是将 head 指向当前正在执行的线程的结构体。
wait_q->tail = _nanokernel.current; 将 tail 也指向当前正在指向的线程的结构体。
对于如下程序:
struct _nano_queue wait_q;_nano_wait_q_init(&wait_q);_nano_wait_q_put(&wait_q);
其在内存空间的存储情况如图 4 所示。
通常,当前正在执行的线程将自己加入到等待线程后,会调用函数 _swip() 释放 CPU,此时处于就绪状态的线程中的优先级最高的线程会占用 CPU 进行执行。
当等待队列不为空时插入线程
当等待队列不为空时,((struct tcs )wait_q->tail) 表示等待队列的队尾线程,((struct tcs )wait_q->tail)->link 表示对待队列的队尾线程的 link 成员。
((struct tcs *)wait_q->tail)->link = _nanokernel.current; 表示将队尾线程的 link 指向当前正在执行的线程的结构体。
wait_q->tail = _nanokernel.current; 表示将 tail 也指向当前正在执行的线程的结构体。
例子
对于如下代码:
struct _nano_queue wait_q;_nano_wait_q_init(&wait_q);//***********************************************************************************///* 线程 1 正在执行 */..._nano_wait_q_put(&wait_q); // 如图 4.../* 线程 1 释放 CPU *///***********************************************************************************///* 线程 2 正在执行, 线程 1 已处于等待队列中 */..._nano_wait_q_put(&wait_q); // 如图 5.../* 线程 2 释放 CPU *///***********************************************************************************///* 线程 3 正在执行, 线程 1/2 已处于等待队列中 */..._nano_wait_q_put(&wait_q); // 如图 6.../* 线程 3 释放 CPU *///***********************************************************************************//...//***********************************************************************************///* 线程 6 正在执行, 线程 1/2/3/4/5 已处于等待队列中 */..._nano_wait_q_put(&wait_q); // 如图 7.../* 线程 6 释放 CPU */
在队首取出线程
struct tcs *_nano_wait_q_remove(struct _nano_queue *wait_q){ return wait_q->head ? _nano_wait_q_remove_no_check(wait_q) : NULL;}
先判断等待队列是否为空,如果为空,返回 NULL,如果不为空,调用函数_nano_wait_q_remove_no_check().
static struct tcs *_nano_wait_q_remove_no_check(struct _nano_queue *wait_q){ // 获取等待队列的队首线程 struct tcs *tcs = wait_q->head; if (wait_q->tail == wait_q->head) { // 如果等待队列中只有一个线程了,对队列进行复位 _nano_wait_q_reset(wait_q); } else { // 如果队列中不止一个线程,将 head 指向第二个线程 wait_q->head = tcs->link; } tcs->link = 0; // 将该线程加入就绪队列 _nano_fiber_ready(tcs); return tcs;}
- Zephyr OS nano 内核篇: 等待队列 wait_q
- Zephyr OS 内核篇:系统启动
- Zephyr OS 内核篇:系统启动
- Zephyr OS 内核篇:系统启动
- Zephyr OS 内核篇: 内核链表
- Zephyr OS 内核篇: 执行上下文
- Zephyr OS 内核篇: 执行上下文
- Zephys OS nano 内核篇:前言
- Zephys OS nano 内核篇:isr 服务
- Zephys OS nano 内核篇:fiber 服务
- Zephys OS nano内核篇:原子操作
- Zephys OS nano内核篇:定时器 Timer
- Zephys OS nano 内核篇:信号量 semaphore
- Zephys OS nano 内核篇:FIFO
- Zephys OS nano 内核篇:LIFO
- Zephys OS nano 内核篇:栈 stack
- Zephyr OS之内核调度
- Zephys OS nano 内核篇:task 服务 - 基础
- Zephyr OS 内核篇: 内核链表
- linux 查看文件命令 file which whereis locate find
- 51Nod-合法整数集(位运算)
- CentOS配置opencv, EasyPR 1.4的环境搭建
- Qt 5 帮助文档的使用
- Zephyr OS nano 内核篇: 等待队列 wait_q
- Quartz Spring与Spring Task总结
- 1613-3-傅溥衍 总结《2016年10月7日》【连续第七天总结】
- Activity传递数据的经典例子
- java Native方法(Object.HashCode())
- Spring-Task用法
- 汉诺塔II(hdu 1207)
- Zephys OS nano内核篇:超时服务timeout
- Zephys OS nano内核篇:定时器 Timer