1

私はこの学校のプロジェクトを持っており、setjmp と longjmp を使用して不正確な計算を行うことについてです。プログラムは、シグナル ハンドラにシグナルを送るタイマーを開始します。

タイマーが切れる前に、いくつかの反復計算があります (デモ用に、何も役に立たないループだけです)。このループの最初に setjmp 呼び出しがあり、シグナル ハンドラに longjmp 呼び出しがあります。これは基本的に、ループが途中で計算を停止し、longjmp を呼び出すシグナル ハンドラを実行するように強制します。

私が抱えている問題は、計算部分が非常に短い場合は常にかなり一貫してセグメンテーション違反をしているように見えることですが、計算部分が長い場合 (内部ループに多くの反復がある場合)、問題なく実行されます (まだセグメンテーション違反に遭遇していません)。 . 明らかに、セグメンテーション違反はその計算セクションの周囲の領域で発生しているに違いありませんが、印刷ステートメントを使用する場合と同様に、デバッグによって状況が変化するため、どこから発生しているのかわかりません。

ここに私が持っているコードがあります:

#include <iostream>
#include <signal.h>
#include <sys/time.h>
#include <setjmp.h>
#include <errno.h>
#include <stdlib.h>

jmp_buf env;


static void usr_timer_tick(int signal)
{
    if(signal == SIGALRM)
    {
        longjmp(env, 1);
    }
}


/*Program Description
 * This program first sets up the timer to signal usr_timer_tick
 * every 1 second on the SIGALRM signal. It then proceeds to do an iterated calculation three times.
 * An infinite loop calls setjmp and when 0 is returned, continues doing
 * a calculation on temp. After an iteration is complete, the result of
 * the iteration is saved into finalResult after blocking SIGALRM to
 * make the saving of the result atomic.
 *
 * Once the signal handler(usr_timer_tick) is triggered, it calls longjmp which forces
 * setjmp to return a non-zero value, which causes the main function to break out
 * of the infinite loop and start a new calculation...this is done a total of 3
 * times for demonstration purposes.
 */
int main(int argc, char **argv)
{

    //init timer using setitimer..real mode
    int which = ITIMER_REAL;
    struct itimerval value;
    struct sigaction sact;
    sigset_t newmask, oldmask;
    sigemptyset( &newmask );
    sigemptyset( &oldmask );
    sigaddset(&newmask, SIGALRM);

    sact.sa_flags = 0;
    sact.sa_handler = usr_timer_tick;
    sigaction( SIGALRM, &sact, NULL );
//    value.it_interval.tv_sec = 0;        /* One second */
//    value.it_interval.tv_usec = 0;
//    value.it_value.tv_sec = 1;           /* One second */
//    value.it_value.tv_usec = 0;
//
//    setitimer(which, &value, NULL);



    double finalResult = 0;
    int loopcount = 0;
    double tempResult = 0;
    for(int j = 0; j < 10; j++)
    {
        loopcount = 0;


            std::cout << "Run " << j << " begin loop "
            << loopcount << "\n";


            if(setjmp(env) == 0)
            {   //timer not hit yet

                //sigprocmask(SIG_BLOCK, &newmask, NULL);
                value.it_interval.tv_sec = 0;        /* One second */
                value.it_interval.tv_usec = 0;
                value.it_value.tv_sec = 1;           /* One second */
                value.it_value.tv_usec = 0;

                setitimer(which, &value, NULL);

                //sigprocmask(SIG_SETMASK, &oldmask, NULL);
                for(;;)
                {
                    //Do some random calculations
                    for(int i = 0; i < 1; i++)
                    {
                        tempResult = tempResult + .001;
                    }

                    //block signal from arriving and save to finalResult
                    if(sigprocmask(SIG_BLOCK, &newmask, NULL) < 0) exit(-1);
                    finalResult = tempResult;
                    std::cout << "Run " << j << " complete loop "
                        << loopcount << " result = " << finalResult<< "\n";
                    loopcount++;
                    if(sigprocmask(SIG_SETMASK, &oldmask, NULL)< 0) exit(errno);
                }
            }
            else
            {
                //timer signal arrived, print the final result and get out of loop
                std::cout << "***Run " << j << " killed on loop "
                        << loopcount << " result = "<< finalResult << "\n";
                sigprocmask(SIG_SETMASK, &oldmask, NULL);
                //break;
            }



    }
    return 0;
}

シグナルハンドラで longjmp を使用することに同意しない人もいるかもしれませんが、これは私の教授がそうするように言った方法です。また、longjmp が呼び出された後に SIGALRM のブロックを解除することに注意してください (main の else ステートメントを参照)。

dmesg を見ると、次のようになります。

 [121395.233842] cppapplication_[17397]: 
segfault at 2 ip b74656f6 sp bfbb5abc 
error 6 in libc-2.12.1.so[b743b000+157000

]

4

2 に答える 2

1

'longjmp' を使用して、タイマーなどの非同期イベントを終了することはできません。これは、通常の呼び出し規則によって保存されるレジスタとスタック ポインターを保存および復元することのみを目的として設計されています。

volatile補足: 7.13.2.1 で指定されているように、ローカル変数でキーワードを使用することを検討してください。

longjmp関数が呼び出された時点で、すべてのアクセス可能なオブジェクトには値があり、抽象マシンの他のすべてのコンポーネントには状態がsetjmpあります。 volatile修飾された型を持たず、setjmp 呼び出しと呼び出しの間で変更されたlongjmpものは不確定です。

于 2011-03-30T19:35:28.067 に答える
0

シグナルハンドラーがを呼び出すlongjmpので、ジャンプターゲットは有効である必要があります。つまり、setjmp最初に呼び出し、次にsigaction、を呼び出しますsetitimer

于 2011-03-30T17:50:19.723 に答える