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

例如,如果线程 1 打印“第三调用线程1 ”并进入睡眠状态,如果我发送信号切换上下文,线程 1 正在睡眠,线程 2 开始执行,现在如果我再次发送信号切换上下文,那么线程 1 再次打印“调用线程 1第三次”。理想情况下,它应该从睡眠中醒来并增加 a 的值,对吗?为什么两次打印相同的值?

这是代码打印的输出:

接收信号 10

从线程2切换到线程1

调用thread2 1次

接收信号 10

从线程1切换到线程2

调用thread1 1次

接收信号 10

从线程2切换到线程1

调用thread2 1次

接收信号 10

从线程1切换到线程2

调用thread1 1次

请帮我解决一下这个。

4

1 回答 1

0

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

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

另一件值得注意的事情是信号处理程序在新的上下文中运行。这使得调用诸如 setcontext 或 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;
}

此外,我替换了while (1);main 函数末尾的 以在 thread1 上启动上下文。

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