前面说过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学习(五)线程和调度的更多相关文章

  1. C++并发与多线程学习笔记--线程之间调度

    condition_variable wait() notify_one notify_all condition_variable 条件变量的实际用途: 比如有两个线程A和B,在线程A中等待一个条件 ...

  2. Zephyr学习(三)启动过程

    一.写在前面 最近对zephyr这个系统很感兴趣,因此业余有时间的时候都在研究它的源码,而光看代码不去动手这不是我的风格,于是乎在网上淘了一块STM32F103C8T6的核心板和一块NRF52832的 ...

  3. Linux的进程线程及调度

    本文为宋宝华<Linux的进程.线程以及调度>学习笔记. 1 进程概念 1.1 进程与线程的定义 操作系统中的经典定义: 进程:资源分配单位. 线程:调度单位. 操作系统中用PCB(Pro ...

  4. [转帖]Linux的进程线程及调度

    Linux的进程线程及调度 本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10393707.html 本文为宋宝华<Linux的进程 ...

  5. python自动化开发学习 进程, 线程, 协程

    python自动化开发学习 进程, 线程, 协程   前言 在过去单核CPU也可以执行多任务,操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换任务2,任务2执行0.01秒,在切换到任务3,这 ...

  6. ucore操作系统学习(五) ucore lab5用户进程管理

    1. ucore lab5介绍 ucore在lab4中实现了进程/线程机制,能够创建并进行内核线程的调度.通过上下文的切换令线程分时的获得CPU,使得不同线程能够并发的运行. 在lab5中需要更进一步 ...

  7. <<Windows via C/C++>>学习笔记 —— 线程优先级【转】

    转自:http://www.cnblogs.com/wz19860913/archive/2008/08/04/1259807.html 每个线程都有一个“优先级”,范围是0-31,0为最低优先级,3 ...

  8. Java多线程-线程的调度(休眠)

    Java线程调度是Java多线程的核心,只有良好的调度,才能充分发挥系统的性能,提高程序的执行效率. 这里要明确的一点,不管程序员怎么编写调度,只能最大限度的影响线程执行的次序,而不能做到精准控制. ...

  9. Java多线程-线程的调度(合并)

    线程的合并的含义就是将几个并行线程的线程合并为一个单线程执行,应用场景是当一个线程必须等待另一个线程执行完毕才能执行时可以使用join方法. join为非静态方法,定义如下:void join(): ...

  10. Java多线程-线程的调度(守护线程)

    本文转自http://www.cnblogs.com/linjiqin/p/3210004.html 感谢作者 守护线程与普通线程写法上基本没啥区别,调用线程对象的方法setDaemon(true), ...

随机推荐

  1. Hibernate简答题

    简单题目 1.持久化对象的三种状态,代表含义. 自由状态(transient): 不曾进行持久化,未与任何Session相关联 持久化状态(persistent): 仅与一个Session相关联 游离 ...

  2. Java 服务端监控方案(四. Java 篇)

    http://jerrypeng.me/2014/08/08/server-side-java-monitoring-java/ 这个漫长的系列文章今天要迎来最后一篇了,也是真正与 Java 有关的部 ...

  3. svn提交报错Previous operation has not finished; run 'cleanup' if it was interrupted

    从SVN上拉下来Document文档(word和excel),在本地修改后,准备通过TortoiseSVN提交,发现报错. Error: Previous operation has not fini ...

  4. Arcmap内容列表刷新

    Arcmap内容列表刷新ILayer pLayer = pFDOGLayer as ILayer;                            if (!pLayer.Visible)    ...

  5. [web 前端] mobx教程(一)-mobx简介

    opy from : https://blog.csdn.net/smk108/article/details/84777649 Mobx是通过函数响应式编程使状态管理变得简单和可扩展的状态管理库.M ...

  6. SQLAlchemy——获取某列为空的数据

    session.query(StockAllInfo).filter( StockAllInfo.ts_code == tsCode and StockAllInfo.py_code==None).a ...

  7. PHP 使用POST 获取不到部分数据问题

    使用PHP开发的一个项目,在测试阶段客户反馈说数据没有保存成功 而我之前测试是通过的,但按客户的信息上却重现了问题 问题是这样的 客户通过浏览器进行保存,但有些数据已经保存成功,但某些数据却没有保存成 ...

  8. Docker 集群Swarm创建和Swarm Web管理

    关于Docker Swarm更多的介绍请查看<Docker管理工具-Swarm部署记录> 一.环境配置 1.安装环境 # cat /etc/redhat-release CentOS Li ...

  9. Python 3安装MySQLdb

    Python 2安装的是mysql-python,Python 3安装mysql-python以后,仍然不能import MySQLdb,原来Python 3应该安装mysqlclient,就可以im ...

  10. C# System.IO.FileMode

    字段 Append 6 若存在文件,则打开该文件并查找到文件尾,或者创建一个新文件. 这需要 Append 权限. FileMode.Append 只能与 FileAccess.Write 一起使用. ...