C 编程/协程
鲜为人知的是,大多数 C 实现都内置了可用于协作式多任务/协程的原语。他们是 setcontext 和 setjmp.
函数setjmp
与longjmp
成对使用,用于将执行转移到代码中的不同位置。它依赖于现有的jmp_buf
声明。
#include <setjmp.h>
int main (void)
{
jmp_buf buf1;
if (setjmp(buf1) == 0)
{
/* This code is executed on the first call to setjmp. */
longjmp(buf1, 1);
} else {
/* This code is executed once longjmp is called. */
}
return 0;
}
setjmp()
将当前执行点存储在内存中,只要包含该函数不返回,该内存就保持有效。它最初返回0
。当longjmp
使用原始jmp_buf
和替换返回值调用时,控制权将返回setjmp
。
请注意,jmp_buf 在没有使用地址运算符的情况下传递给 setjmp。
理解 setjmp 和 longjmp 的最简单方法是,setjmp 会存储 CPU 的状态,包括程序计数器、堆栈指针、所有寄存器(包括标志寄存器的位),在由 jmp_buf 指向的位置,该位置定义为 LEN+1,这足以存储所涉及的任何 CPU 的寄存器。longjmp(buf) 从不返回,因为它从由之前的 setjmp 调用设置的结构 jmp_buf buf 的内容中恢复 CPU,因此执行从 setjmp 被调用之后开始,但 setjmp 的返回值不是 0,而是 longjmp 中第二个参数使用的任何值。这类似于 fork() 系统调用,它返回 0 给子进程,并将子进程的 PID 返回给父进程。
互联网建议协程对于将软件实现为协作状态机很有用,例如词法分析器处理输入文本并发出标记,以便解析器可以决定存储标记并请求下一个标记,或对其当前的标记集采取行动。这并不是多线程程序在数据上同步,如果出现错误,忘记获取锁会导致竞争条件,而是使用 setjmp 和 longjmp,似乎是协作进程,保证一次只运行一个进程,无需担心上下文切换唤醒睡眠进程(使用单独的 jmp_buf 静态位置,每个进程可以在调用 longjmp 后调用 setjmp 获取自己的 jmp_buf,如果返回 0,或者继续循环处理非零返回值的共享数据)。