Переключение контекста выполняет один и тот же оператор дважды
Я пытаюсь узнать, как работает переключение контекста и как заставить ваш процесс переключать контекст после получения определенного сигнала. Вот мой код
#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 времени" дважды.
Например, если thread1 печатает "вызывающий thread1 для 3-го раза" и переходит в спящий режим, а thread1 находится в спящем режиме, если я посылаю сигнал для переключения контекста, thread2 начинает выполняться, теперь, если я снова отправляю сигнал для переключения контекста, то thread1 снова печатает "вызывающий thread1" в третий раз ". В идеале он должен выйти из сна и увеличить значение a, правильно? Почему он печатает одно и то же значение дважды?
Вот вывод, напечатанный кодом:
принятый сигнал 10
переключение с потока 2 на поток 1
вызов thread2 в течение 1 раза
принятый сигнал 10
переключение с нити 1 на нить 2
вызов thread1 в течение 1 раза
принятый сигнал 10
переключение с потока 2 на поток 1
вызов thread2 в течение 1 раза
принятый сигнал 10
переключение с нити 1 на нить 2
вызов thread1 в течение 1 раза
Пожалуйста, помогите мне с этим.
1 ответ
Несмотря на незначительность, члену uc_link элемента ucontext_t должно быть присвоено значение перед передачей в makecontext.
В противном случае из справочной страницы Linux setcontext вызовет функцию, которая была передана в makecontext. Это означает, что каждый раз, когда вызывается setcontext, не сохраняется никакой информации о предыдущем выполнении ни thread1_fun, ни thread2_fun. Вот почему одно и то же сообщение печатается несколько раз. При предыдущем вызове функция находилась в спящем режиме до того, как счетчик мог быть увеличен. При следующем вызове функция печатает неизмененное значение. С другой стороны, swapcontext сохранит текущий контекст в первом аргументе и активирует контекст во втором аргументе.
Также следует отметить, что обработчик сигнала работает в новом контексте. Это затрудняет вызов таких функций, как setcontext или swapcontext. Смотрите обсуждение здесь о swapcontext и сигналах. Как предлагается в этой ссылке, флаг типа sig_atomic_t может использоваться для сигнализации текущего состояния выполнения в thread1_fun и thread2_fun для вызова swapcontext.
Вот новая переменная сигнала, объявленная прямо под include:
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);
в конце основной функции для запуска контекста в thread1.
ucontext_t main_thread;
if (swapcontext(&main_thread, &thread1) == -1) {
perror("swapcontext");
exit(EXIT_FAILURE);
}
return 0;