Newsletter 001.「温故知新」
周末偶然看到这篇 Coroutines in C,作者利用达夫设备(Duff’s Device)定义了几个宏,在 C 语言中模拟了协程的实现。然后又警告读者不要在公司写这样的代码 😅:
Of course, this trick violates every coding standard in the book. Try doing this in your company’s code and you will probably be subject to a stern telling off if not disciplinary action! . . . It’s a wonder you haven’t been fired on the spot for such irresponsible coding practice. You should be ashamed of yourself.
甚至连达夫本人都吐槽道 🥲:
The “revolting way to use switches to implement interrupt driven state machines”
原理就是利用 C 语言的两个特性实现:
switch
和case
支持和for
,while
循环语句交叉 : 所以switch
语句支持直接跳转到循环内部
|
|
- 全局变量保存栈变量和需要跳转的位置:
static int i, state = 0;
一个完整的例子如下:
|
|
看完觉得不过瘾,又去搜了下 Stackful / Stackless Coroutine 的区别,在 Hack News 的讨论里点进去看了几篇关于 Coroutine 的论文 🙄:
- Revisiting Coroutines:对 coroutine 的分类和概念的定义
- Symmetric/Asymmetric coroutine: Asymmetric coroutine 提供两个操作:一个调用 coroutine,另一个挂起 coroutine。挂起操作将程序控制流返回给调用者;Symmetric coroutine 只提供一个操作,就是将控制流切换到其他 coroutine,并只在同一层级进行调度;
- Stackful/Stackless coroutine: Stackful 表示每个 coroutine 都有独立的栈,每次挂起和恢复都需要保存寄存器变量到栈中。
- Coroutines in Lua:Lua 实现的 coroutine 是 Stackful 和 Asymmetric coroutine
所以上面利用达夫设备实现的 coroutine 是 Stackless 和 Asymmetric 的 coroutine。
然后想起自己大学的时候在 AVR MPU 上写过一个 Coroutine 的实现。嗯。。。就去 Review 了一下自己 3 年前的代码,提了几个 Issue 就跑 🤔。主要是对几个概念进行澄清:
- cooperative multitasking 和 preemptive multitasking:
也就是说,ROS 不是 cooperative multitasking,而(算)是 preemptive multitasking:
- 低优先级的任务会被高优先级的无条件抢占
- 在任务的 priority 相等时,ROS 采用 round-robin 根据时间片调度
- 低优先级任务可能一直得不到调度,除非高优先级任务主动 suspend
- 换个角度,对于高优先级任务来说是 cooperative multitasking:除非主动 suspend ,否则不会被抢占。
- 对于低优先级任务来说是 preemptive multitasking:会被高优先级的无条件抢占
- Symmetric coroutine 和 Asymmetric coroutine:
需要声明的是, ROS 实现的协程是 symmetric coroutine。因此任务不能嵌套调用(不存在调用者和被调用者的关系),任务都是在同一层级进行调度的。
-
ROS 实现的是 stackful coroutine,为每个任务申请单独的栈。
-
目前 ROS 提供的系统调用有如下四个:
ros_init()
: 初始化系统时钟等ros_create_task(*task, task_func, priority, stack, stack_size)
: 创建任务ros_delay(ticks)
: 挂起任务一段时间,主动让出 CPUros_schedule()
: 任务调度,不必主动调用
系统调用的功能十分有限和
简洁简陋,后续会考虑提供更多系统调用,丰富任务间协同通信的能力:ros_yield(x)
: 挂起任务并返回值,唤醒任意其他的任务ros_join(*task)
: 等待其他任务结束- Channel: 任务间数据传递,类似 Go 中的
chan
- Mutex: 锁
完。