4

我在上下文切换中获得了一些乐趣。我已将示例代码复制到文件 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 上下文上下文.c -g

关于获取、制作、交换上下文被弃用的问题。嗯。

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

我尝试使用 gdb,但是一旦我进入交换上下文,它就是空白的。它不会跳入 f1。我只是一直按回车键,它只会将光标移动到控制台上的新行吗?

知道发生了什么吗?与使用 Mac/弃用方法有关吗?

谢谢

4

1 回答 1

9

看起来您的代码只是从ucontext 文档中复制/粘贴的,这一定令人沮丧,因为它不起作用......

据我所知,你的筹码太小了。对于您的堆栈,我无法让它使用小于 32KiB 的大小。

尝试进行以下更改:

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

// . . .

    char st1[STACK_SIZE];
    char st2[STACK_SIZE];

是的,修好了。为什么它修复了它?

好吧,让我们再深入研究一下这个问题。首先,让我们了解一下实际情况。

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

如果你使用一些调试器(一定要使用 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;
}

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

显然,根据给定的堆栈大小正在发生一些奇怪的事情,所以让我们寻找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;
}

嗯,这很好!是什么MINSIGSTKSZ?好吧,让我们看一下signal.h

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

显然,这些值实际上是 POSIX 标准的一部分。尽管我在 ucontext 文档中没有看到任何引用这些值的内容,但我想这是隐含的,因为ucontext 保留了当前的信号掩码

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

有趣的是,MINSIGSTKSZ在 os x 上是 32768 字节,但在我的 linux 机器上只有 2048 字节,这就解释了为什么它可以在 linux 上工作,但不能在 os x 上工作。

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

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

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

于 2016-11-01T03:31:16.107 回答