上下文切换 - makecontext和swapcontext在这里工作(OSX)

Pro*_*rof 2 c macos context-switch ucontext

我对上下文切换很开心.我已将示例代码复制到文件 http://pubs.opengroup.org/onlinepubs/009695399/functions/makecontext.html

我为OSX定义了宏_XOPEN_SOURCE.

#define _XOPEN_SOURCE 
#include <stdio.h>
#include <ucontext.h>


static ucontext_t ctx[3];


static void
f1 (void)
{
    puts("start f1");
    swapcontext(&ctx[1], &ctx[2]);
    puts("finish f1");
}


static void
f2 (void)
{
    puts("start f2");
    swapcontext(&ctx[2], &ctx[1]);
    puts("finish f2");
}


int
main (void)
{
    char st1[8192];
    char st2[8192];


    getcontext(&ctx[1]);
    ctx[1].uc_stack.ss_sp = st1;
    ctx[1].uc_stack.ss_size = sizeof st1;
    ctx[1].uc_link = &ctx[0];
    makecontext(&ctx[1], f1, 0);


    getcontext(&ctx[2]);
    ctx[2].uc_stack.ss_sp = st2;
    ctx[2].uc_stack.ss_size = sizeof st2;
    ctx[2].uc_link = &ctx[1];
    makecontext(&ctx[2], f2, 0);


    swapcontext(&ctx[0], &ctx[2]);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我建造它

gcc -o context context.c -g

关于get,make,swap context被弃用的我.咩.

当我运行它只是挂起.它似乎没有崩溃.它只是挂起.

我尝试使用gdb,但是一旦我进入swapcontext,它就是空白.它没有跳进f1.我只是继续按Enter键,它只是将光标移动到控制台上的新行?

知道发生了什么事吗?处理Mac /弃用方法有什么关系?

谢谢

Dao*_*Wen 7

看起来您的代码只是从ucontext文档中复制/粘贴,这必然会令它感到沮丧,因为它无法工作......

据我所知,你的筹码太小了.我无法使用任何低于32KiB的堆栈.

尝试进行这些更改:

#define STACK_SIZE (1<<15) // 32KiB

// . . .

    char st1[STACK_SIZE];
    char st2[STACK_SIZE];
Run Code Online (Sandbox Code Playgroud)

是的修好了.为什么要修复它呢?

好吧,让我们再深入研究一下这个问题.首先,让我们看看实际发生了什么.

当我运行它只是挂起.它似乎没有崩溃.它只是挂起.

如果你使用一些debugger-fu(确保使用lldb-gdb只是在os x上不能正常工作),那么你会发现当应用程序"挂起"时,它实际上是在你的main函数中的一个奇怪的循环中旋转,在下面的评论中的箭头说明.

int
main (void)
{
    char st1[8192];
    char st2[8192];


    getcontext(&ctx[1]);
    ctx[1].uc_stack.ss_sp = st1;
    ctx[1].uc_stack.ss_size = sizeof st1;
    ctx[1].uc_link = &ctx[0];
    makecontext(&ctx[1], f1, 0);


    getcontext(&ctx[2]);// <---------------------+ back to here
    ctx[2].uc_stack.ss_sp = st2;//               |
    ctx[2].uc_stack.ss_size = sizeof st2;//      |
    ctx[2].uc_link = &ctx[1];//                  |
    makecontext(&ctx[2], f2, 0); //              |
    //                                           |
    puts("about to swap...");//                  |
    //                                           |
    swapcontext(&ctx[0], &ctx[2]);// ------------+ jumps from here
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

请注意,我puts在循环中间添加了一个额外的调用.如果你添加那行并再次编译/运行,那么你将会看到它开始喷出"about to swap..." 无限字符串而不是程序.

基于给定的堆栈大小显然会发生一些棘手的问题,所以让我们只看一下ss_size被引用的所有地方......

(注意:Apple ucontext实现的权威源代码位于https://opensource.apple.com/source/,但是我会使用GitHub镜像,因为它更适合搜索和链接.)

如果我们看一下makecontext.c,我们会看到这样的事情:

if (ucp->uc_stack.ss_size < MINSIGSTKSZ) {
   // fail without an error code since makecontext is a void function
   return;
}
Run Code Online (Sandbox Code Playgroud)

嗯,那很好!什么是MINSIGSTKSZ?好吧,我们来看看signal.h:

#define MINSIGSTKSZ 32768   /* (32K)minimum allowable stack */
#define SIGSTKSZ    131072  /* (128K)recommended stack size */
Run Code Online (Sandbox Code Playgroud)

显然,这些值实际上是POSIX标准的一部分.虽然我在ucontext文档中没有看到任何引用这些值的内容,但我认为这有点暗示,因为ucontext会保留当前的信号掩码.

无论如何,这解释了我们所看到的棘手行为.由于makecontext由于堆栈大小太小而导致调用失败,因此调用getcontext(&ctx[2])是设置内容的内容ctx[2],因此调用swapcontext(&ctx[0], &ctx[2])最终会再次交换回该行,从而创建无限循环...

有趣的是,MINSIGSTKSZ在os x上是32768字节,但在我的linux盒子上只有2048字节,这解释了为什么它适用于linux而不是os x.

基于所有这些,看起来更安全的选择是使用建议的堆栈大小sys/signal.h:

char st1[SIGSTKSZ];
char st2[SIGSTKSZ];
Run Code Online (Sandbox Code Playgroud)

那,或切换到不被弃用的东西.如果你不反对C++,你可以看一下Boost.Context.