0

コンテキスト切り替えがどのように機能するか、および特定のシグナルを受信した後にプロセスがコンテキストを切り替える方法を学ぼうとしています。これが私のコードです

#include<stdio.h>
#include<stdlib.h>
#include<ucontext.h>
#include<signal.h>
#include<sys/time.h>
#include<sys/types.h>
#include<unistd.h>

#define STACK_SIZE 4096

static ucontext_t thread1, thread2;

void thread1_fun() {
    static int a = 1;
    while (1) {
        printf("calling thread1 for %d time\n", a);
        sleep(5);
        a > 20 ? a = 0 : a++;
    }
}

void thread2_fun() {
    static int a = 1;
    while (1) {
        printf("calling thread2 for %d time\n", a);
        sleep(5);
        a > 20 ? a = 0 : a++;
    }
}

void sig_handler(int signal) {
    static int curr_thread = 0;
    printf("received signal %d\n", signal);
    if (curr_thread == 1) {
        curr_thread = 0;
        printf("switching from thread1 to thread2\n");
        setcontext(&thread1);
    } else {
        curr_thread = 1;
        printf("switching from thread2 to thread1\n");    
        setcontext(&thread2);
    }
}

int main() {
    int i = 0;
    struct sigaction act;
    act.sa_handler = sig_handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    sigaction(SIGUSR1, &act, NULL);
    /* sigaction(SIGTERM, &act, NULL); */

    getcontext(&thread1);
    thread1.uc_stack.ss_sp = malloc (STACK_SIZE);
    thread1.uc_stack.ss_size = STACK_SIZE;
    thread1.uc_stack.ss_flags = 0;
    makecontext(&thread1, thread1_fun, 0);

    getcontext(&thread2);
    thread2.uc_stack.ss_sp = malloc (STACK_SIZE);
    thread2.uc_stack.ss_size = STACK_SIZE;
    thread2.uc_stack.ss_flags = 0;
    makecontext(&thread2, thread2_fun, 0);

    printf("%d\n", getpid());
    while (1);
}

ここで、ターミナルから「kill -s SIGUSR1」コマンドを実行します。プロセスは、このシグナルを受信した後にコンテキストを切り替えますが、問題は、'calling thread for %d time'を 2 回出力することです。

たとえば、スレッド 1 が ' calling thread1 for 3rd time ' を出力してスリープ状態になり、スレッド 1 がスリープしている間にシグナルを送信してコンテキストを切り替えると、スレッド 2 が実行を開始し、もう一度シグナルを送信してコンテキストを切り替えると、スレッド 1 が再び「calling thread1」を出力する場合3回目」。理想的には、スリープ状態から抜け出し、a の値をインクリメントする必要がありますね。同じ値を 2 回出力するのはなぜですか?

コードで印刷された出力は次のとおりです。

受信信号 10

スレッド 2 からスレッド 1 への切り替え

thread2 を 1 回呼び出す

受信信号 10

スレッド 1 からスレッド 2 への切り替え

スレッド1を1回呼び出す

受信信号 10

スレッド 2 からスレッド 1 への切り替え

thread2 を 1 回呼び出す

受信信号 10

スレッド 1 からスレッド 2 への切り替え

スレッド1を1回呼び出す

これで私を助けてください。

4

1 に答える 1

0

マイナーではありますが、ucontext_t の uc_link メンバーには、 makecontext に渡す前に値を割り当てる必要があります

それ以外の場合、Linux のマニュアル ページから、setcontextは makecontext に渡された関数を呼び出します。これは、setcontext が呼び出されるたびに、thread1_fun または thread2_fun の前回の実行に関する情報が保存されないことを意味します。これが、同じメッセージが複数回出力される理由です。前回の呼び出しでは、カウンターがインクリメントされる前に、関数はスリープ状態でした。次の呼び出しで、関数は変更されていない値を出力します。一方、swapcontext は、現在のコンテキストを最初の引数に保存し、2 番目の引数でコンテキストをアクティブにします。

もう 1 つの注意点は、シグナル ハンドラーが新しいコンテキストで実行されることです。これにより、setcontext や swapcontext などの関数を呼び出すことが困難になります。swapcontext とシグナルについては、こちらのディスカッションを参照してください。そのリンクで示唆されているように、タイプ sig_atomic_t のフラグを使用して、thread1_fun および thread2_fun の現在の実行状態に通知し、swapcontext を呼び出すことができます。

インクルードのすぐ下で宣言された新しいシグナル変数を次に示します。

static sig_atmoic_t switch_context = 0;

更新された thread1_fun は次のとおりです (thread2_fun は、thread1 と thread2 が入れ替わったものと同様です)。

void thread1_fun() {
    static int a = 1;
    while (1) {
        printf("calling thread1 for %d time\n", a);
        sleep(5);
        a > 20 ? a = 0 : a++;
        if(switch_context) {
            switch_context = 0;
            swapcontext(&thread1, &thread2);
        }
    }
}

新しい sig_handler は次のとおりです。

void sig_handler(int signal) {
    static int curr_thread = 1;
    printf("received signal %d\n", signal);
    if (curr_thread == 1) {
        curr_thread = 0;
        printf("switching from thread1 to thread2\n");
    } else {
        curr_thread = 1;
        printf("switching from thread2 to thread1\n");
    }
    switch_context = 1;
}

さらに、main 関数の最後にある を置き換えて、スレッド 1while (1);でコンテキストを開始しました。

ucontext_t main_thread;
if (swapcontext(&main_thread, &thread1) == -1) {
    perror("swapcontext");
    exit(EXIT_FAILURE);
}
return 0;
于 2016-09-13T02:03:08.530 に答える