11

setjmp/longjmpを調査していたところ、 setjmpが命令ポインター、スタック ポインターなどのレジスターを保存することがわかりました...

ただし、ここで得られないのは、スレッド自体のスタック内のデータをsetjmplongjmpの呼び出しの間で変更できないということです。その場合、longjmpは期待どおりに動作しません。

明確にするために、たとえば、longjmpがスタック ポインターを復元するとき、スタック ポインターが現在指しているメモリ内のデータは、setjmpが呼び出されたときと同じではないとします。これは起こりえますか?そうなったら困りませんか?

また、「 setjmp() ルーチンを呼び出したルーチンが戻った後、longjmp() ルーチンを呼び出すことはできません。」というステートメントが意味することは何ですか。

4

4 に答える 4

6

スタックポインタは、スタックの「使用済み」部分と「未使用」部分の間の分割を示します。を呼び出すとsetjmp、現在のすべての呼び出しフレームは「使用済み」側にあり、呼び出しsetjmpた関数がsetjmp戻る前に、の後に行われる呼び出しはすべて、保存されたスタックポインターの「未使用」側にあります。呼び出しlongjmpた関数setjmpが戻った後に呼び出すと、未定義の動作が呼び出されるため、ケースを考慮する必要がないことに注意してください。

現在、既存の呼び出しフレームの一部のローカル変数は、呼び出し元の関数またはポインターのいずれかによって変更される可能性があります。これが、多くの場合setjmpに使用する必要がある理由の1つです...volatile

于 2011-11-01T16:16:59.523 に答える
6

setjmp()/longjmp()スタックを保存するためのものではありませんsetcontext()/getcontext()

標準では、と の呼び出しsetjmp()の間で変更された呼び出しを行う関数で定義された不揮発性の自動変数の値は、 の後に指定されていないと規定されています。これと同じ理由で、呼び出し方にもいくつかの制限があります。setjmp()longjmp()longjmp()setjmp()

于 2011-11-01T16:10:51.907 に答える
2

Cのsetjmp/longjmp(以降slj)機能は見苦しく、その動作は実装によって異なる場合があります。それにもかかわらず、例外がないことを考えると、Cではsljが必要になることがあります(C ++はsljよりもほぼすべての点で優れた例外を提供し、sljは多くのC ++機能とうまく相互作用しないことに注意してください)。

sljを使用する場合、ルーチンParent()がルーチンSetter()を呼び出し、次にsetjmp()を呼び出し、次にJumperを呼び出してlongjmp()を呼び出すと仮定すると、次の点に注意する必要があります。

  1. コードは、longjmpが実行されることなくsetjmpが実行されるスコープを合法的に終了する場合があります。ただし、スコープが終了するとすぐに、以前に作成されたjmp_bufは無効と見なされる必要があります。コンパイラはおそらくそれをそのようにマークするために何もしませんが、それを使用しようとすると、任意のアドレスへのジャンプを含む、予測できない動作が発生する可能性があります。
  2. Jumper()のローカル変数は、longjmp()の呼び出しで蒸発し、それらの値は無関係になります。
  3. 制御がParentに戻るときはいつでも、どのような方法でも、Parentのローカル変数は、そのような変数のアドレスが取得され、そのようなポインターを使用して変更されない限り、Setterを呼び出したときの状態になります。いずれの場合も、setjmp/longjmpはそれらの値にまったく影響を与えません。そのような変数のアドレスが取得されていない場合、setjmp()がそのような変数の値をキャッシュし、longjmp()がそれらを復元する可能性があります。ただし、そのシナリオでは、変数がキャッシュされたときと復元されたときの間で変数を変更する方法がないため、キャッシュ/復元による目に見える影響はありません。
  4. Setterの変数は、setjmp()呼び出しによってキャッシュされる場合とされない場合があります。longjmp()呼び出しの後、そのような変数は、setjmp()が実行されたときの値、最終的にlongjmp()を呼び出すルーチンを呼び出したときの値、またはそれらの任意の組み合わせを持つ可能性があります。少なくとも一部のC方言では、そのような変数は、キャッシュされないように「揮発性」と宣言される場合があります。

setjmp / longjmp()が役立つ場合もありますが、非常に危険な場合もあります。ほとんどの場合、未定義動作を引き起こす保護エラーコードはなく、多くの実際のシナリオでは、不適切な使用が悪いことを引き起こす可能性があります(実際の結果がしばしば何と一致する可能性があるある種の未定義動作とは異なります)プログラマーが意図した)。

于 2011-11-01T16:28:14.623 に答える
0

以下の例では、setjmp / longjumpは、mainに存在するiの値をポインターを介して変更します。forループでインクリメントされることはありません。さらに楽しくするには、1992年のIOCCCのhttp://www.ioccc.org/years-spoiler.html受賞者であるalbert.cのエントリを参照してください。(私がCソースを読んでROTFLした数少ない回数の1つ...)

#include <stdio.h>
#include <setjmp.h>

jmp_buf the_state;

void helper(int *p);
int main (void)
{
int i;

for (i =0; i < 10;    ) {
    switch (setjmp (the_state) ) {
    case 0:  helper (&i) ; break;
    case 1:    printf( "Even=\t"); break;
    case 2:    printf( "Odd=\t"); break;
    default: printf( "Oops=\t"); break;
        }
    printf( "I=%d\n", i);
    }

return 0;
}
void helper(int *p)
{
*p += 1;
longjmp(the_state, 1+ *p%2);
}
于 2011-11-01T16:28:25.653 に答える