【问题标题】:Context switching executes same statement twice上下文切换两次执行相同的语句
【发布时间】:2017-01-19 03:07:30
【问题描述】:

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

#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' 两次。

例如,如果 thread1 打印“calling thread1 for 3rd time”并进入睡眠状态,而当我发送信号切换上下文时 thread1 处于睡眠状态,则 thread2 开始执行,现在如果我再次发送信号来切换上下文,那么 thread1 再次打印 'calling thread1 for 3rd time'。理想情况下,它应该从睡眠中醒来并增加 a 的值,对吗?为什么要打印两次相同的值?

这是代码打印的输出:

收到信号 10

从线程2切换到线程1

调用thread2 1次

收到信号 10

从线程1切换到线程2

调用thread1 1次

收到信号 10

从线程2切换到线程1

调用thread2 1次

收到信号 10

从线程1切换到线程2

调用thread1 1次

请帮帮我。

【问题讨论】:

    标签: c linux multithreading signals ucontext


    【解决方案1】:

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

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

    另一件值得注意的事情是信号处理程序在新的上下文中运行。这使得调用诸如 setcontext 或 swapcontext 之类的函数变得有问题。请参阅关于交换上下文和信号的讨论 here。正如该链接中所建议的,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 函数末尾的 while (1); 以在 thread1 上启动上下文。

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

    【讨论】:

      最近更新 更多