上下文切换执行两次相同的语句

时间:2016-09-11 15:02:50

标签: c linux multithreading signals ucontext

我正在尝试了解上下文切换的工作原理以及如何在收到特定信号后制作流程切换上下文。这是我的代码

#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'。该过程在收到此信号后切换上下文,但问题是它打印'调用线程%d time'两次。

例如,如果thread1打印'第三时间调用thread1 '并进入休眠状态,并且当我发送信号以切换上下文时thread1正在休眠,则thread2开始执行,现在如果我再次发送信号到切换上下文,则thread1再次打印'调用thread1 3rd time'。理想情况下,它应该从睡眠中恢复并增加a的值,正确吗?为什么两次打印相同的值?

以下是代码打印的输出:

  

收到信号10

     

从thread2切换到thread1

     

调用thread2一次

     

收到信号10

     

从thread1切换到thread2

     

调用thread1一次

     

收到信号10

     

从thread2切换到thread1

     

调用thread2一次

     

收到信号10

     

从thread1切换到thread2

     

调用thread1一次

请帮助我。

1 个答案:

答案 0 :(得分:0)

虽然是次要的,但是在传递给makecontext之前,应该为ucontext_t的uc_link成员分配一个值。

否则,在Linux手册页中,setcontext将调用传递给makecontext的函数。这意味着每次调用setcontext时,都不会保存有关先前执行thread1_fun或thread2_fun的信息。这就是多次打印相同消息的原因。在上一次调用时,该函数在计数器可以递增之前处于休眠状态。在下一次调用时,该函数将打印未更改的值。另一方面,swapcontext将当前上下文保存在第一个参数中,并激活第二个参数中的上下文。

另一个需要注意的是信号处理程序在新的上下文中运行。这使得调用setcontext或swapcontext等函数成为问题。请参阅有关swapcontext和signal的讨论here。正如该链接中所建议的那样,可以使用sig_atomic_t类型的标志来通知thread1_fun和thread2_fun中的当前执行状态以调用swapcontext。

这是在包含正下方声明的新信号变量:

static sig_atmoic_t switch_context = 0;

这是更新的thread1_fun(thread2_fun是simlar,其中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函数末尾的while (1);以在thread1上启动上下文。

ucontext_t main_thread;
if (swapcontext(&main_thread, &thread1) == -1) {
    perror("swapcontext");
    exit(EXIT_FAILURE);
}
return 0;