-
Notifications
You must be signed in to change notification settings - Fork 1
文档:多任务和调度
Contiki-NG采用协作式多任务处理样式的原始Contiki事件驱动模型。 对抢占式多线程的支持已被删除,这使得Contiki-NG是严格非抢占式的操作系统。
Contiki-NG中的应用程序通常是使用Process抽象编写的。 进程建立在称为Protothreads的轻量级线程库的顶部。
在典型的执行场景中,进程(或原型线程)将空闲,直到接收到事件为止。 在接收到事件时,该进程将通过执行相应的工作块来使用它。然后,它将暂停自己的执行,直到接收到下一个事件为止。 一些示例事件是:
- 定时器到期
- 接收传入的网络数据包
- 通过串行线路接收消息
- 用户操作,例如按下按钮
- 传感器驱动器指示有新读数可用
- 用户定义的事件
由于Contiki-NG调度程序是协作的(非抢占式的),因此它将永远不会在正在运行的进程(或protothread)与另一个进程之间进行上下文切换。 因此,为了使事情正常工作,每个正在运行的原型线程都必须在完成任务后自愿将控制权交还给调度程序。 因此,对于开发人员来说,重要的是要确保进程不会过多地控制控件,并且将长时间的操作拆分为多个进程调度,以使此类操作可以在它们最后一次停止时恢复。
可以在doc:processes中找到有关如何实现流程和事件的更多信息。
Contiki-NG模型使用两种类型的事件:异步和同步。
- 异步事件被放入队列中,并以循环方式分派给接收进程。异步事件可以发布到特定进程,也可以广播。在后一种情况下,内核会将事件分派给所有进程。无法指定进程将接收广播事件的顺序。
- 同步事件使接收过程立即得到安排。通常,这将导致发布过程的执行停止,直到该事件的相应进程执行结束为止,此时发布过程将恢复。同步事件无法广播。 Contiki-NG还支持轮询机制。轮询是调度到单个接收进程(无法广播)的特定类型的高优先级异步事件。
内核将在异步事件分派之前以及之间调度所有轮询的进程。如果是广播事件,则在将事件分发到两个连续的进程之间也将进行轮询。
Contiki-NG多任务模型的非抢先、合作性质意味着,进程永远不会被调度程序意外中断。 但是,进程的执行可能(并且经常会)被硬件中断处理程序意外中断。 中断处理程序本身可以被优先级较高的中断的处理程序抢占。
考虑到这一点,可以根据在其中执行上下文来对Contiki-NG代码进行分类:
- 代码始终在中断上下文之外运行; 我们将其称为“主”(或“主线程”)上下文,
- (总是或有时)在中断上下文中运行的代码。
始终在主上下文中运行的代码的实现要简单得多,例如不需要可重入。
同步访问共享资源在很大程度上取决于代码的哪些部分访问它们。
当仅从主线程上下文中访问共享资源时,开发人员无需依赖任何同步原语,因为调度程序不会意外中断对资源的读/写操作。
当可以从中断上下文内部以及外部访问共享资源时,事情就变得更加复杂。 在这种情况下,开发人员需要确保资源状态保持一致。 为此,Contiki-NG提供了一些基本的同步原语(例如互斥体和关键部分),这些基本原语在doc:synch-primitives以及在线API文档中有详细记录。
考虑到以上所有内容,在开发可能在中断上下文中运行的代码时,需要格外小心。中断处理程序开发人员需要记住,以下Contiki-NG系统和库函数在中断上下文中运行不安全:
- 发布事件:在中断上下文中调用
process_post()
和process_post_synch()
不安全。如果中断处理程序需要对进程进行调度,则应使用轮询机制,而不要调用process_poll()
。 - 某些数据结构操作库在中断上下文中不安全使用。这包括:
- 主链表库(
list。[ch]
) - 队列库(
queue.h
) - 堆栈库(
stack.h
) - 循环链表库(
circular-list.[ch]
) - 双链列表库(
dbl-list.[ch]
) - 循环的双向链接列表库(
dbl-circ-list.[ch]
)
- 主链表库(
- 调度定时器:事件计时器(
etimer
),回调计时器(ctimer
)和trick流计时器(trickle-timer
)库依赖于列表操作。因此,在中断上下文中操纵事件和回调计时器是不安全的 - 邻居表操作:
nbr.[hc]
中的所有功能 - 看门狗定时器永远不应该在中断上下文中刷新:从中断内部调用
watchdog_periodic()
可能会导致设备永远不会从固件崩溃中恢复,因为其WDT在中断中被频繁刷新了