在 OSX 下使用 makecontext
官网文档的例子的时候,发现会进入一个死循环:
下面是完整代码(附注释):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 #define _XOPEN_SOURCE #include <ucontext.h> #include <stdio.h> #include <stdlib.h> static ucontext_t uctx_main, uctx_func1, uctx_func2;#define handle_error(msg) \ do { perror(msg); exit(EXIT_FAILURE); } while (0) static void func1 (void ) { printf ("func1: started\n" ); printf ("func1: swapcontext(&uctx_func1, &uctx_func2)\n" ); if (swapcontext(&uctx_func1, &uctx_func2) == -1 ) handle_error("swapcontext" ); printf ("func1: returning\n" ); } static void func2 (void ) { printf ("func2: started\n" ); printf ("func2: swapcontext(&uctx_func2, &uctx_func1)\n" ); if (swapcontext(&uctx_func2, &uctx_func1) == -1 ) handle_error("swapcontext" ); printf ("func2: returning\n" ); } int main (int argc, char *argv[]) { char func1_stack[16384 ]; char func2_stack[16384 ]; if (getcontext(&uctx_func1) == -1 ) handle_error("getcontext" ); uctx_func1.uc_stack.ss_sp = func1_stack; uctx_func1.uc_stack.ss_size = sizeof (func1_stack); uctx_func1.uc_link = &uctx_main; makecontext(&uctx_func1, func1, 0 ); if (getcontext(&uctx_func2) == -1 ) handle_error("getcontext" ); uctx_func2.uc_stack.ss_sp = func2_stack; uctx_func2.uc_stack.ss_size = sizeof (func2_stack); uctx_func2.uc_link = (argc > 1 ) ? NULL : &uctx_func1; makecontext(&uctx_func2, func2, 0 ); printf ("main: swapcontext(&uctx_main, &uctx_func2)\n" ); if (swapcontext(&uctx_main, &uctx_func2) == -1 ) handle_error("swapcontext" ); printf ("main: exiting\n" ); exit (EXIT_SUCCESS); }
当在 OSX 下执行的时候,会无限输出
main: swapcontext(&uctx_main, &uctx_func2)
。
详细解答在: https://stackoverflow.com/questions/40299849/context-switching-is-makecontext-and-swapcontext-working-here-osx
OSX 的 makecontext
里面实现存在的问题:
源码:https://github.com/Apple-FOSS-Mirror/Libc/blob/2ca2ae74647714acfc18674c3114b1a5d3325d7d/x86_64/gen/makecontext.c
MINSIGSTKSZ
过大(32K),我们设置的栈大小只有 16K。
1 2 3 4 5 6 7 8 9 10 else if ((ucp->uc_stack.ss_sp == NULL ) || (ucp->uc_stack.ss_size < MINSIGSTKSZ)) { ucp->uc_mcsize = 0 ; }
总结来说就是:
OSX 下的 MINSIGSTKSZ
为 32K,然后 OSX 里面的
makecontext
实现判断 ucontext_t
的栈大小小于
MINSIGSTKSZ
的时候,会直接返回,但是这个时候我们的调用是失败的, 因为成功的
makecontext
调用应该会将 ucontext_t
里面的
rip
修改为 func2
的地址。(rip
是保存下一条需要执行的指令的地址的寄存器)。
getcontext
的作用是初始化一个
ucontext_t
,初始化之后,这个 ucontext_t
指向
getcontext
调用的下一条指令(汇编层面的指令)。
结果就导致当我们调用 swapcontext
的时候,使用
uctx_func2
来恢复上下文环境的时候,实际上执行的是
getcontext
的下一条指令。然后后面调用
makecontext
仍然失败,从而无限循环。
解决方法
将栈的大小设置为 32K 或者更大,也就是把源码里面的 16384 修改为
32768。