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

时间:2016-10-28 07:08:24

标签: 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;
}

我建立它

gcc -o context context.c -g

关于get,make,swap context被弃用的问题。 MEH。

当我跑它时,它只是挂起。它似乎没有崩溃。它只是挂起。

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

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

由于

1 个答案:

答案 0 :(得分:7)

看起来您的代码只是从the ucontext documentation进行了复制/粘贴,这必然让它感到沮丧,因为它不起作用......

据我所知,你的筹码太小了。我无法让你的筹码少于32KiB。

尝试进行这些更改:

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

// . . .

    char st1[STACK_SIZE];
    char st2[STACK_SIZE];
  是的,修好了。为什么它会修复它?

好吧,让我们再深入研究一下这个问题。首先,让我们找出实际发生的事情。

  

当我跑它时,它只是挂起。它似乎没有崩溃。它只是挂起。

如果你使用一些debugger-fu(确保使用lldb-gdb只是不能在os x上正常工作),那么你会发现当应用程序是&#34;挂起&#34;时,它& #39; s实际上在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;
}

请注意,我在循环中间添加了一个额外的puts调用。如果您添加该行并再次编译/运行,那么只需挂起您的程序,就会看到它开始喷出字符串"about to swap..." ad infinitum

显然基于给定的堆栈大小会发生一些棘手的事情,所以让我们只是寻找引用ss_size的所有地方...

(注意:Apple ucontext实施的权威源代码位于https://opensource.apple.com/source/,但是我会使用a GitHub mirror take a look at makecontext.c,因为它更适合搜索和链接。)

如果我们a look in signal.h,我们会看到类似的内容:

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

嗯,真好!什么是MINSIGSTKSZ?好吧,让我们来part of the POSIX standard

#define MINSIGSTKSZ 32768   /* (32K)minimum allowable stack */
#define SIGSTKSZ    131072  /* (128K)recommended stack size */

显然这些值实际上是ucontext preserves the current signal mask。虽然我在ucontext文档中没有看到任何引用这些值的内容,但我认为这是Boost.Context以来的暗示。

无论如何,这解释了我们所看到的棘手行为。由于makecontext调用由于堆栈大小太小而失败,因此调用getcontext(&ctx[2])即设置ctx[2]的内容,因此调用swapcontext(&ctx[0], &ctx[2])最后再次换回那条线,创造无限循环......

有趣的是,{x}上的MINSIGSTKSZ是32768字节,但我的linux盒子上只有2048字节,这解释了为什么它可以在linux上运行而不是os x。

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

char st1[SIGSTKSZ];
char st2[SIGSTKSZ];

那,或切换到不被弃用的东西。如果您不反对C ++,可以查看{{3}}。