为什么用户级线程(纤程)的实现需要为每个纤程分配一个新的堆栈?
Why does the implementation of user level thread (fiber) require a new allocated stack per fiber?
在 C 中,纤程可以使用 setjmp()
和 longjmp()
进行编码,以在用户级别实现上下文切换。
如中所述
evanjones.ca and Portable Multithreading(pdf)还要求每个fiber都有一个新分配的stack。
由于纤程存在于线程上下文中,当它被调用时会自动关联一个堆栈帧,那么为什么它需要这个新分配的堆栈呢? : 当一根光纤要切换到另一根光纤时,可以使用以下方法:
cpu_context[N] :global array where the i-th entry is the cpu context(jmp_buffer) of the i-th fiber
fiber_ith :
[...]
if ( setjmp(cpu_context[i]) == 0 ){
longjmp(cpu_context[j])
}
[...]
新堆栈的必要性是由于 所写的事实,无法使用 longjmp()
返回堆栈框架不再有效的纤程执行从光纤调用 longjmp()
?
的那一刻起
编辑:这些光纤必须是非抢先的并且可以自愿从一根光纤切换到另一根光纤
假设一个 fiber 有一个函数调用另一个函数,另一个函数调用另一个函数,然后导致该 fiber 切换为另一个 fiber。当我们恢复这个纤程时,我们需要确保所有局部变量都回到纤程切换时的状态,我们需要确保从这个函数返回到调用函数。
因此出现以下规则:
- 当纤程为 运行 时,它可以更改其堆栈。
- 恢复光纤时,堆栈必须回到光纤切换时的位置。
从这两条规则可以看出,每根光纤都必须有自己的堆栈。
首先,线程不打算手动控制自己的调度。也就是说,线程不会决定结束或暂停其执行并启动另一个线程。相反,线程可供 运行 同时使用,并且系统可以同时 运行 不同物理或虚拟处理器上的多个线程,或者可以启动和停止线程以便它们共享处理器上的可用时间。
因为线程可以同时运行,所以它们必须有单独的栈。每个线程可能会在其他线程正在更改和使用它们的堆栈的同时更改和使用自己的堆栈。这些堆栈必须分开以避免冲突。
其次,setjmp
和 longjmp
并非用于跳转到线程状态中的任意点。在同一个线程中,程序可以仅使用 longjmp
到 return 到其当前调用堆栈中的前一个 setjmp
。例如,你不能做一些工作,用 setjmp
记住状态 A
,再做一些工作,用 setjmp
记住状态 B
,然后 longjmp
声明 A
,然后 longjmp
声明 B
。在 longjmp
到 A
之后,B
不再可用 - setjmp
为其保存的内容不再有效。
在 C 中,纤程可以使用 setjmp()
和 longjmp()
进行编码,以在用户级别实现上下文切换。
如中所述
evanjones.ca and Portable Multithreading(pdf)还要求每个fiber都有一个新分配的stack。
由于纤程存在于线程上下文中,当它被调用时会自动关联一个堆栈帧,那么为什么它需要这个新分配的堆栈呢? : 当一根光纤要切换到另一根光纤时,可以使用以下方法:
cpu_context[N] :global array where the i-th entry is the cpu context(jmp_buffer) of the i-th fiber
fiber_ith :
[...]
if ( setjmp(cpu_context[i]) == 0 ){
longjmp(cpu_context[j])
}
[...]
新堆栈的必要性是由于 longjmp()
返回堆栈框架不再有效的纤程执行从光纤调用 longjmp()
?
编辑:这些光纤必须是非抢先的并且可以自愿从一根光纤切换到另一根光纤
假设一个 fiber 有一个函数调用另一个函数,另一个函数调用另一个函数,然后导致该 fiber 切换为另一个 fiber。当我们恢复这个纤程时,我们需要确保所有局部变量都回到纤程切换时的状态,我们需要确保从这个函数返回到调用函数。 因此出现以下规则:
- 当纤程为 运行 时,它可以更改其堆栈。
- 恢复光纤时,堆栈必须回到光纤切换时的位置。
从这两条规则可以看出,每根光纤都必须有自己的堆栈。
首先,线程不打算手动控制自己的调度。也就是说,线程不会决定结束或暂停其执行并启动另一个线程。相反,线程可供 运行 同时使用,并且系统可以同时 运行 不同物理或虚拟处理器上的多个线程,或者可以启动和停止线程以便它们共享处理器上的可用时间。
因为线程可以同时运行,所以它们必须有单独的栈。每个线程可能会在其他线程正在更改和使用它们的堆栈的同时更改和使用自己的堆栈。这些堆栈必须分开以避免冲突。
其次,setjmp
和 longjmp
并非用于跳转到线程状态中的任意点。在同一个线程中,程序可以仅使用 longjmp
到 return 到其当前调用堆栈中的前一个 setjmp
。例如,你不能做一些工作,用 setjmp
记住状态 A
,再做一些工作,用 setjmp
记住状态 B
,然后 longjmp
声明 A
,然后 longjmp
声明 B
。在 longjmp
到 A
之后,B
不再可用 - setjmp
为其保存的内容不再有效。