0

私は問題があります。タイマーと SIGALRM を使用して ucontext スレッドを切り替えるプログラムを実装する必要がありますが、evict_thread 関数を使用してスレッドを切り替えると、セグメンテーション エラーが発生します。プログラムの実行中にさまざまな時点で発生するため、競合状態の結果であると思います。これが私のevict_threadです

void evict_thread(int signal)
{   
// Check that there is more than one thread in the queue
if ((int)list_length(runqueue) > 1)
{
    // Remove the currently executing thread from the runqueue and store its id
    int evict_thread_id = list_shift_int(runqueue);

    // Place the thread at the back of the run queue
    list_append_int(runqueue, evict_thread_id);

    // Get the id of the thread that is now at the head of the run queue
    int exec_thread_id = list_item_int(runqueue, 0);

    // Set the start time for new thread to the current time
    clock_gettime(CLOCK_REALTIME, &thread_table[exec_thread_id]->start);

    printf("Switching context from %s to %s\n",
        thread_table[evict_thread_id]->thread_name,
        thread_table[exec_thread_id]->thread_name);

    // Execute the thread at the head of the run queue
    if (swapcontext(&thread_table[evict_thread_id]->context, &thread_table[exec_thread_id]->context) == -1)
    {
        perror("swapcontext failed\n");
        printf("errno: %d.\n", errno);
        return;
    }   
}
return;     
}

上記の関数は、次の方法で呼び出されます

// Set the SIGALRM
if (sigset(SIGALRM, evict_thread) == -1)
{
    perror("sigset failed\n");
    printf("errno: %d.\n", errno);
    return;
}

// Initialize timer
thread_switcher.it_interval.tv_sec  = 0;
thread_switcher.it_interval.tv_usec = quantum_size;
thread_switcher.it_value.tv_sec = 0;
thread_switcher.it_value.tv_usec =  quantum_size;
setitimer(ITIMER_REAL, &thread_switcher, 0);

実行キューは、ucontext スレッドへのポインターのグローバル テーブルへのインデックスである整数の単なるグローバル リストです。リストは、libslack.org で入手可能な C 汎用ユーティリティ ライブラリのリスト データ構造を使用して実装されます。

タイマーを無効にして、コンテキストを切り替える前に各スレッドを最後まで実行すると、プログラムは正常に実行されますが、実行中にスレッドが切り替えられると、約 80% の確率でセグメンテーション エラーが発生します。

また、gdb を使用してセグメンテーション違反をバックトレースしようとすると、システムコール内で発生したと表示されます。

4

3 に答える 3

0

推測として、コンテキストを切り替えたためにカーネルに表示されないものをカーネルに渡しています。あなたはセグメンテーション違反について質問していますが、あなたのコードは面白いことをしています。

おそらく、スレッドスケジューリングのより標準的なモデルを検討した場合、問題を回避できます。コンテキストスイッチを使用してスレッドをスケジュールする代わりに、これを行う他の方法があります。また、まったく同じ現在のプログラムモデルを使用して、エビクトスレッドからそれらを呼び出すことができます。

この提案のいくつかは、システム固有のものです。お使いのOSを教えていただければ、状況に合ったものを見つけることができます。または、自分でチェックすることもできます。

POSIXスレッドスケジューリングについて読んでください。モデルで機能するSCHED_FIFOに特に注意してください。

https://computing.llnl.gov/tutorials/pthreads/man/sched_setscheduler.txt

これは一般的に、POSIXスレッドライブラリを使用してスレッドをスケジュールする場合に当てはまります。難しい方法で実行しようとするのではありません。

于 2013-03-14T00:10:35.320 に答える
0

どのように機能させるかについてアドバイスすることはできませんが、機能していない点についていくつかの点を以下に示します。

シグナル ハンドラーは、他のコードに関して非同期的に実行されます。たとえば、何らかのコードが を更新しているrunqueueときにシグナルが発生し、シグナル ハンドラが実行 list_append_int(runqueue, evict_thread_id); されると、かなり深刻な競合状態が発生します。

printf()シグナルハンドラーで呼び出さないでください。デッドロックまたはさらに悪化する可能性があります。 シグナル ハンドラーで安全に呼び出すことができる関数の一覧を次に示します。setcontext/swapcontext は、シグナル ハンドラーで安全に呼び出すことができるとは言及されていませんが、その Linux のマニュアル ページには、シグナル ハンドラーで setcontext() を呼び出すことができると記載されています。

setcontext() のマンページの内容にも注意してください。

シグナルが発生すると、現在のユーザー コンテキストが保存され、カーネルによってシグナル ハンドラー用の新しいコンテキストが作成されます。

そのため、swapcontext() を発行すると、シグナルが発生する前に実行されていた現在のコンテキストではなく、シグナル ハンドラーのコンテキストが保存されている可能性があります。

于 2013-03-14T00:04:24.660 に答える
0

シグナル ハンドラーはメイン コードに対して非同期で実行されることに注意してください。このman 7 signalページは、ガイドラインを順守していることを確認するために注意深く読む価値があります。たとえば、このセクションでは、や などの他の機能Async-signal-safe-functionsについては言及されていません。つまり、これらの関数をシグナル ハンドラーから確実に呼び出すことはできません。printfswapcontext

一般に、シグナル ハンドラでの作業はできるだけ少なくするようにしてください。通常、これはsig_atomic_tシグナル ハンドラで型のフラグを設定し、メイン ループでこのフラグの状態をチェックすることを意味します。

おそらく、シグナル ハンドラからではなく、メイン ループでコンテキスト切り替えが発生するように、コードを再配置してください。sigwaitタイマー信号を待つためにメインループで使用できる場合があります。

于 2013-03-14T00:04:32.923 に答える