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 コンテキスト context.c -g

get、make、swap コンテキストが非推奨になっていることについて私に翼をつけます。うーん。

実行するとハングします。クラッシュしないようです。ハングするだけです。

gdb を使用してみましたが、一度 swapcontext に入ると空白になります。f1にはジャンプしません。Enterキーを押し続けると、カーソルがコンソールの新しい行に移動するだけですか?

何が起こっているのか分かりますか?Mac/deprecate メソッドでの作業と何か関係がありますか?

ありがとう

4

1 に答える 1

9

あなたのコードはucontext documentationからコピー/貼り付けされているように見えます.

私が知る限り、あなたのスタックは小さすぎます。32KiB 未満のスタックでは動作しませんでした。

これらの変更を行ってみてください:

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

// . . .

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

うん、それを修正しました。なぜそれを修正したのですか?

さて、問題をもう少し掘り下げてみましょう。まず、実際に何が起こっているのかを調べてみましょう。

実行するとハングします。クラッシュしないようです。ハングするだけです。

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;
}

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は現在のシグナル mask を保持するため、暗示されていると思います。

とにかく、これは私たちが見ている厄介な動作を説明しています. makecontextスタックサイズが小さすぎるために呼び出しが失敗しているため、 への呼び出しgetcontext(&ctx[2])は の内容を設定しているctx[2]ため、 への呼び出しはswapcontext(&ctx[0], &ctx[2])再びその行にスワップバックして無限ループを作成します...

興味深いことに、MINSIGSTKSZOS x では 32768 バイトですが、私の Linux ボックスでは 2048 バイトしかありません。

そのすべてに基づいて、推奨されるスタックサイズを使用するのがより安全なオプションのようですsys/signal.h:

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

それ、または廃止されていないものに切り替えます。C++ が嫌いでない場合は、Boost.Contextを参照してください。

于 2016-11-01T03:31:16.107 に答える