1

ユーザー レベルのスレッド ライブラリを実装しようとしていますが、ラウンド ロビン方式でスレッドをスケジュールする必要があります。現在、makecontext、getcontext、および swapcontext を使用して作成した 2 つのスレッドの切り替えを機能させようとしています。ITIMER_PROF 値を持つ setitimer が使用され、SIGPROF シグナルが生成されるたびに新しいスレッドをスケジュールするためのハンドラーが sigaction に割り当てられます。ただし、シグナル ハンドラは呼び出されないため、スレッドはスケジュールされません。その理由は何ですか?コードの一部を次に示します。

    void userthread_init(long period){
    /*long time_period = period;
//Includes all the code like initializing the timer and attaching the signal
// handler function "schedule()" to the signal SIGPROF. 
// create a linked list of threads - each thread's context gets added to the list/updated in the list
// in userthread_create*/

    struct itimerval it;
    struct sigaction act;
    act.sa_flags = SA_SIGINFO;
    act.sa_sigaction = &schedule;
    sigemptyset(&act.sa_mask);
    sigaction(SIGPROF,&act,NULL);
    time_period = period;
    it.it_interval.tv_sec = 4;
    it.it_interval.tv_usec = period;
    it.it_value.tv_sec = 1;
    it.it_value.tv_usec = 100000;
    setitimer(ITIMER_PROF, &it,NULL);
    //for(;;);
}

上記のコードは、タイマーを初期化し、ハンドラー スケジュールをシグナル ハンドラーにアタッチするものです。scheduler() 関数を呼び出す上記の関数にシグナル SIGPROF が与えられると想定しています。スケジューラ関数は次のとおりです。

void schedule(int sig, siginfo_t *siginf, ucontext_t* context1){
    printf("\nIn schedule");
    ucontext_t *ucp = NULL;
    ucp = malloc(sizeof(ucontext_t));
    getcontext(ucp);
    //ucp = &sched->context;
    sched->context = *context1;
    if(sched->next != NULL){

        sched = sched->next;
    }
    else{
        sched = first;
    }
    setcontext(&sched->context);
}   

それぞれのコンテキストが格納されている準備完了スレッドのキューがあります。setcontext 命令が実行されるたびに、各スレッドをスケジュールする必要があります。ただし、scheduler() は呼び出されません。誰かが私の間違いを指摘できますか??

4

1 に答える 1

2

コードを見た後、この回答を完全に修正します。いくつかの問題があります:

  • いくつかのコンパイラ警告があります
  • スレッド作成メソッドの外側または内側ではなく、スレッド ID を初期化することは決してないので、コードが機能することにも驚いています!
  • gtthread_create() 関数で初期化されていないメモリから読み取っています。OSX と Linux の両方でテストしました。OSX ではクラッシュし、Linux では奇跡的に初期化されました。
  • いくつかの場所では、malloc() を呼び出し、別のものへのポインタで上書きします - メモリ リーク
  • スレッドは終了後にリンク リストから削除されないため、ルーチンの終了後に奇妙なことが起こっています。

while(1) ループを追加すると、schedule() が呼び出されてスレッド 2 から出力されるのがわかりますが、スレッド 1 は消えてしまいます (おそらく初期化されていないスレッド ID が原因です)。大規模なコードのクリーンアップが必要だと思います。

これが私が提案するものです:

  • コンパイラの警告をすべて修正します— たとえそれらが問題ではないと思っていても、ノイズによって何かが失われる可能性があります (互換性のないポインター型など)。-Wall & -pedantic; でコンパイルしています。それは良いことです。次のステップに進み、修正してください。
  • \n を printf ステートメントの先頭ではなく末尾に置きます— 2 つのスレッドは stdout に出力していますが、フラッシュされていないので見えません。printf("\nMessage");通話を次のように変更しますprintf("Message\n");
  • Valgrindを使用してメモリの問題を検出します。valgrind は、C/C++ 開発に使用する中で最も優れたツールです。apt-get & yum から入手できます。running の代わりに./test1runvalgrind ./test1を実行すると、メモリの破損、メモリ リーク、初期化されていない読み取りなどが強調表示されます。ヴァルグリンドはすごい。
  • システム コールが値を返す場合は、それを確認します — コードで、getcontext、swapcontext、sigaction、setitimer のすべての戻り値を確認します
  • スケジューラ (または任意のシグナル ハンドラ) から async-signal-safe メソッドのみを呼び出します。これまでのところ、スケジューラ内から malloc() と printf() を修正しました。signal(7) の man ページを確認してください- 「Async-signal-safe functions」を参照してください。
  • コードをモジュール化します— リンク リストの実装はより整然としたものになる可能性があります。分離されていれば、1) スケジューラのコードが少なくなり、よりシンプルになります。2) スケジューラ コードをデバッグすることなく、リンク リストの問題を分離できます。

もうすぐそこに到達するので、それを続けてください。ただし、次の 3 つの簡単なルールを覚えておいてください。

  1. いつでもきれいに
  2. コンパイラの警告を修正したままにする
  3. 奇妙なことが起こっているときは、valgrind を使用してください

幸運を!


古い答え:

システムコールの戻り値を確認する必要があります。それが答えを見つけるのに役立つかどうかにかかわらず、とにかくそれを行うべきです:)

sigaction() の戻り値を確認し、-1 の場合は errno を確認してください。sigaction() は、いくつかの理由で失敗する可能性があります。シグナル ハンドラーが起動しない場合は、セットアップされていない可能性があります。

編集: setitimer() の戻り値も確認してください!

編集 2: ちょっと考えただけですが、malloc() を削除してみてください。malloc はシグナルセーフではありません。例: このように:

void schedule(int sig, siginfo_t *siginf, ucontext_t* context1){
    printf("In schedule\n");
    getcontext(&sched->context);
    if(sched->next != NULL){
        sched = sched->next;
    }
    else{
        sched = first;
    }
    setcontext(&sched->context);
}   

編集 3:このディスカッションによると、シグナル ハンドラ内で printf() を使用することはできません。非同期シグナルに対して安全なwrite()呼び出しに置き換えることができます。

// printf("In schedule\n");
const char message[] = "In schedule\n";
write( 1, message, sizeof( message ) );
于 2014-02-03T05:44:38.210 に答える