Zephyr学习(五)线程和调度
前面说过zephyr支持静态和动态两种方式创建线程,这里分析动态创建的方式。应用程序通过调用k_thread_create()函数创建一个线程,实际上是调用_impl_k_thread_create()函数,定义在zephyr-zephyr-v1.13.0\kernel\thread.c:
k_tid_t _impl_k_thread_create(struct k_thread *new_thread,
k_thread_stack_t *stack,
size_t stack_size, k_thread_entry_t entry,
void *p1, void *p2, void *p3,
int prio, u32_t options, s32_t delay)
{
__ASSERT(!_is_in_isr(), "Threads may not be created in ISRs"); _setup_new_thread(new_thread, stack, stack_size, entry, p1, p2, p3,
prio, options); if (delay != K_FOREVER) {
schedule_new_thread(new_thread, delay);
} return new_thread;
}
第9行,调用_setup_new_thread()函数,在开发环境搭建里已经分析过了。
第12行,传进来的最后一个参数一般为K_NO_WAIT,即马上参与调度,所以if条件成立。
第13行,调用schedule_new_thread()函数,定义在zephyr-zephyr-v1.13.0\kernel\thread.c:
static void schedule_new_thread(struct k_thread *thread, s32_t delay)
{
if (delay == ) {
k_thread_start(thread);
} else {
s32_t ticks = _TICK_ALIGN + _ms_to_ticks(delay);
int key = irq_lock(); _add_thread_timeout(thread, NULL, ticks);
irq_unlock(key);
}
}
第3行,由于K_NO_WAIT的值就为0,所以if条件成立。
第4行,调用k_thread_start()函数,定义在zephyr-zephyr-v1.13.0\kernel\thread.c:
void _impl_k_thread_start(struct k_thread *thread)
{
int key = irq_lock(); /* protect kernel queues */ if (_has_thread_started(thread)) {
irq_unlock(key);
return;
} _mark_thread_as_started(thread);
_ready_thread(thread);
_reschedule(key);
}
第5行,判断线程的状态是否不为_THREAD_PRESTART,如果是则直接返回。
第10行,清除线程的_THREAD_PRESTART状态。
第11行,前面已经分析过了。
第12行,调用_reschedule()函数,定义在zephyr-zephyr-v1.13.0\kernel\sched.c:
int _reschedule(int key)
{
if (_is_in_isr()) {
goto noswap;
} if (_get_next_ready_thread() != _current) {
return _Swap(key);
} noswap:
irq_unlock(key);
return ;
}
第3行,调用_is_in_isr()函数,判断是否处于中断上下文,在中断里是不允许线程切换的,定义在arch\arm\include\kernel_arch_func.h:
#define _is_in_isr() _IsInIsr()
实际上调用的是_IsInIsr()函数,定义在zephyr-zephyr-v1.13.0\arch\arm\include\cortex_m\exc.h:
static ALWAYS_INLINE int _IsInIsr(void)
{
u32_t vector = __get_IPSR(); /* IRQs + PendSV (14) + SYSTICK (15) are interrupts. */
return (vector > ) || (vector && !(SCB->ICSR & SCB_ICSR_RETTOBASE_Msk));
}
即IPSR的值(当前中断号)大于13则认为是处于中断上下文。
回到_reschedule()函数,第7行,调用_get_next_ready_thread()函数,定义在zephyr-zephyr-v1.13.0\kernel\include\ksched.h:
static ALWAYS_INLINE struct k_thread *_get_next_ready_thread(void)
{
return _ready_q.cache;
}
前面也说过,_ready_q.cache始终指向的是下一个要投入运行的线程。
所以,如果当前线程不是下一个要投入的线程,那么第8行,调用_Swap()函数,定义在zephyr-zephyr-v1.13.0\kernel\include\kswap.h:
static inline unsigned int _Swap(unsigned int key)
{
unsigned int ret;
_update_time_slice_before_swap(); ret = __swap(key); return ret;
}
第4行,调用_update_time_slice_before_swap()函数,定义在zephyr-zephyr-v1.13.0\kernel\sched.c:
void _update_time_slice_before_swap(void)
{
/* Restart time slice count at new thread switch */
_time_slice_elapsed = ;
}
即将时间片清0,重新开始累加。
回到_Swap()函数,第6行,调用__swap()函数,定义在zephyr-zephyr-v1.13.0\arch\arm\core\swap.c:
unsigned int __swap(int key)
{
/* store off key and return value */
_current->arch.basepri = key;
_current->arch.swap_return_value = _k_neg_eagain; /* set pending bit to make sure we will take a PendSV exception */
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk; /* clear mask or enable all irqs to take a pendsv */
irq_unlock(); return _current->arch.swap_return_value;
}
第4~5行,保存key和返回值。
第8行,置位pendsv中断。
第11行,使能中断,此时就会产生pendsv中断。
下面分析pendsv中断的处理流程,定义在zephyr-zephyr-v1.13.0\arch\arm\core\swap_helper.S:
SECTION_FUNC(TEXT, __pendsv) /* protect the kernel state while we play with the thread lists */ movs.n r0, #_EXC_IRQ_DEFAULT_PRIO
msr BASEPRI, r0 /* load _kernel into r1 and current k_thread into r2 */
ldr r1, =_kernel
ldr r2, [r1, #_kernel_offset_to_current] /* addr of callee-saved regs in thread in r0 */
ldr r0, =_thread_offset_to_callee_saved
add r0, r2 /* save callee-saved + psp in thread */
mrs ip, PSP stmia r0, {v1-v8, ip} /*
22 * Prepare to clear PendSV with interrupts unlocked, but
23 * don't clear it yet. PendSV must not be cleared until
24 * the new thread is context-switched in since all decisions
25 * to pend PendSV have been taken with the current kernel
26 * state and this is what we're handling currently.
27 */
ldr v4, =_SCS_ICSR
ldr v3, =_SCS_ICSR_UNPENDSV /* _kernel is still in r1 */ /* fetch the thread to run from the ready queue cache */
ldr r2, [r1, _kernel_offset_to_ready_q_cache] str r2, [r1, #_kernel_offset_to_current] /*
39 * Clear PendSV so that if another interrupt comes in and
40 * decides, with the new kernel state baseed on the new thread
41 * being context-switched in, that it needs to reschedules, it
42 * will take, but that previously pended PendSVs do not take,
43 * since they were based on the previous kernel state and this
44 * has been handled.
45 */ /* _SCS_ICSR is still in v4 and _SCS_ICSR_UNPENDSV in v3 */
str v3, [v4, #] /* Restore previous interrupt disable state (irq_lock key) */
ldr r0, [r2, #_thread_offset_to_basepri]
movs.n r3, #
str r3, [r2, #_thread_offset_to_basepri] /* restore BASEPRI for the incoming thread */
msr BASEPRI, r0 /* load callee-saved + psp from thread */
add r0, r2, #_thread_offset_to_callee_saved
ldmia r0, {v1-v8, ip} msr PSP, ip /* exc return */
bx lr
CortexM进入中断时,CPU会自动将8个寄存器(XPSR、PC、LR、R12、R3、R2、R1、R0)压栈。
第5~6行,相当于调用irq_lock()函数。
第9~19行的作用就是将剩下的其他寄存器(R4、R5、R6、R7、R8、R9、R10、R11、PSP)也压栈。
第28~29行,准备清pendsv中断标志。
第34~36行,r2指向下一个要投入运行的线程,其中第36行,将_current指向要投入运行的线程。
第48行,清pendsv中断标志。(不清也可以?)
第51~53行,清要投入运行线程的basepri变量的值。
第56行,恢复BASEPRI寄存器的值。
第59~62行,恢复R4、R5、R6、R7、R8、R9、R10、R11、PSP寄存器。
第65行,中断返回,自动将XPSR、PC、LR、R12、R3、R2、R1、R0寄存器弹出,即切换到下一个线程。
应用程序也可以调用k_yield()函数主动让出CPU,定义在zephyr-zephyr-v1.13.0\kernel\sched.c:
void _impl_k_yield(void)
{
__ASSERT(!_is_in_isr(), ""); if (!_is_idle(_current)) {
LOCKED(&sched_lock) {
_priq_run_remove(&_kernel.ready_q.runq, _current);
_priq_run_add(&_kernel.ready_q.runq, _current);
update_cache();
}
} if (_get_next_ready_thread() != _current) {
_Swap(irq_lock());
}
}
里面的函数都已经分析过了,这里不再重复。
要成功将自己切换出去(让出CPU)的前提是有优先级比自己更高的并且已经就绪的线程。
接下来看一下线程的取消过程。应用程序调用k_thread_cancel()函数取消一个线程,定义在
zephyr-zephyr-v1.13.0\kernel\thread.c:
int _impl_k_thread_cancel(k_tid_t tid)
{
struct k_thread *thread = tid; unsigned int key = irq_lock(); if (_has_thread_started(thread) ||
!_is_thread_timeout_active(thread)) {
irq_unlock(key);
return -EINVAL;
} _abort_thread_timeout(thread);
_thread_monitor_exit(thread); irq_unlock(key); return ;
}
第7~8行,如果线程都没开始运行过,则返回出错。如果线程不是在等待(延时或者休眠),也返回出错,即线程不能自己取消自己。
第13行,调用_abort_thread_timeout()函数,定义在zephyr-zephyr-v1.13.0\kernel\include\timeout_q.h:
static inline int _abort_thread_timeout(struct k_thread *thread)
{
return _abort_timeout(&thread->base.timeout);
}
实际上调用的是_abort_timeout()函数,定义在zephyr-zephyr-v1.13.0\kernel\include\timeout_q.h:
static inline int _abort_timeout(struct _timeout *timeout)
{
if (timeout->delta_ticks_from_prev == _INACTIVE) {
return _INACTIVE;
} if (!sys_dlist_is_tail(&_timeout_q, &timeout->node)) {
sys_dnode_t *next_node =
sys_dlist_peek_next(&_timeout_q, &timeout->node);
struct _timeout *next = (struct _timeout *)next_node; next->delta_ticks_from_prev += timeout->delta_ticks_from_prev;
}
sys_dlist_remove(&timeout->node);
timeout->delta_ticks_from_prev = _INACTIVE; return ;
}
第3行,如果线程没有在延时或者休眠,则返回出错。
第7行,如果线程不是在超时队列的最后,则if条件成立。
第9行,取出线程的下一个节点。
第12行,将下一个节点的延时时间加上要取消的线程剩余的延时时间。
第14行,将线程从超时队列移除。
第15行,将线程的delta_ticks_from_prev设为_INACTIVE。
好了,到这里线程的创建、取消和调度过程都分析完了。
搞明白最近这三篇随笔,也就基本搞懂了zephyr内核的核心内容了,剩下的mutex互斥锁、工作队列、信号量等内容也就比较容易理解了。
Zephyr学习(五)线程和调度的更多相关文章
- C++并发与多线程学习笔记--线程之间调度
condition_variable wait() notify_one notify_all condition_variable 条件变量的实际用途: 比如有两个线程A和B,在线程A中等待一个条件 ...
- Zephyr学习(三)启动过程
一.写在前面 最近对zephyr这个系统很感兴趣,因此业余有时间的时候都在研究它的源码,而光看代码不去动手这不是我的风格,于是乎在网上淘了一块STM32F103C8T6的核心板和一块NRF52832的 ...
- Linux的进程线程及调度
本文为宋宝华<Linux的进程.线程以及调度>学习笔记. 1 进程概念 1.1 进程与线程的定义 操作系统中的经典定义: 进程:资源分配单位. 线程:调度单位. 操作系统中用PCB(Pro ...
- [转帖]Linux的进程线程及调度
Linux的进程线程及调度 本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10393707.html 本文为宋宝华<Linux的进程 ...
- python自动化开发学习 进程, 线程, 协程
python自动化开发学习 进程, 线程, 协程 前言 在过去单核CPU也可以执行多任务,操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换任务2,任务2执行0.01秒,在切换到任务3,这 ...
- ucore操作系统学习(五) ucore lab5用户进程管理
1. ucore lab5介绍 ucore在lab4中实现了进程/线程机制,能够创建并进行内核线程的调度.通过上下文的切换令线程分时的获得CPU,使得不同线程能够并发的运行. 在lab5中需要更进一步 ...
- <<Windows via C/C++>>学习笔记 —— 线程优先级【转】
转自:http://www.cnblogs.com/wz19860913/archive/2008/08/04/1259807.html 每个线程都有一个“优先级”,范围是0-31,0为最低优先级,3 ...
- Java多线程-线程的调度(休眠)
Java线程调度是Java多线程的核心,只有良好的调度,才能充分发挥系统的性能,提高程序的执行效率. 这里要明确的一点,不管程序员怎么编写调度,只能最大限度的影响线程执行的次序,而不能做到精准控制. ...
- Java多线程-线程的调度(合并)
线程的合并的含义就是将几个并行线程的线程合并为一个单线程执行,应用场景是当一个线程必须等待另一个线程执行完毕才能执行时可以使用join方法. join为非静态方法,定义如下:void join(): ...
- Java多线程-线程的调度(守护线程)
本文转自http://www.cnblogs.com/linjiqin/p/3210004.html 感谢作者 守护线程与普通线程写法上基本没啥区别,调用线程对象的方法setDaemon(true), ...
随机推荐
- 深入理解JVM(8)——类加载的时机
一.类的生命周期 一个类从加载进内存到卸载出内存一共要经历7个阶段:加载—>验证—>准备-->解析—>初始化—>使用—>卸载. 类加载包括五部分:加载—>验证 ...
- HDU 5985 Lucky Coins 数学
Lucky Coins 题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=5985 Description Bob has collected a lot ...
- 多个ip以逗号分隔
/^(((?:(?:1[0-9][0-9]\.)|(?:2[0-4][0-9]\.)|(?:25[0-5]\.)|(?:[1-9][0-9]\.)|(?:[0-9]\.)){3}(?:(?:1[0-9 ...
- python之函数联系
----------------------作业一 # 有两个列表,分别存放来老男孩报名学习linux和python课程的学生名字# linux=['钢弹','小壁虎','小虎比','alex','w ...
- table 变量
table 变量的行为类似于局部变量,有明确定义的作用域.该作用域为声明该变量的函数.存储过程或批处理. 在存储过程中使用 table 变量与使用临时表相比,减少了存储过程的重新编译量涉及表变量的事务 ...
- 爬虫程序获取登录Cookie信息时遇到302,怎么处理
最近要做个爬虫程序爬爬东西,先搞定登录授权这块,没得源代码,所以只能自行搞定了,按平时的直接发起HttpWebRequest(req)请求,带上用户名密码,好了,然后 HttpWebResponse ...
- GMA Round 1 最大值
传送门 最大值 求$f(x)=cos(x)+\sqrt{cos^2(x)-4\sqrt{3}cos(x)+4\sqrt{2}sin(x)+10}$的最大值.保留到小数点后3位. $f(x)+\sqrt ...
- 广告行业中常说的 CPC,CPM,CPD,CPT,CPA,CPS 等词的意思是什么?
广告投放流程主要分为展示和转化,CPC/CPM/CPD/CPT/CPA/CPS等代表的是不同的结算模式 展示端的结算方式有: CPM(Cost Per Mille) 每千人成本:只要向足够量级的用户展 ...
- 卷积的三种模式:full, same, valid
通常用外部api进行卷积的时候,会面临mode选择. 本文清晰展示三种模式的不同之处,其实这三种不同模式是对卷积核移动范围的不同限制. 设 image的大小是7x7,filter的大小是3x3 1,f ...
- 【Java】maven多项目资源共享
方案一: <resources> <resource> <!-- <directory>${project.parent.relativePath}/../. ...