我想写一段代码,每10微秒在线程之间切换一次。但问题出在收益函数上。我在运行计时器处理程序时被中断。所以它没有正确完成。这是我初始化计时器的代码:
signal(SIGALRM, &time_handler);
struct itimerval t1;
t1.it_interval.tv_sec = INTERVAL_SEC;
t1.it_interval.tv_usec = INTERVAL_USEC;
t1.it_value.tv_sec = INTERVAL_SEC;
t1.it_value.tv_usec = INTERVAL_USEC;
setitimer(ITIMER_REAL, &t1, NULL);
这是处理程序函数的代码:
void time_handler(int signo)
{
write(STDOUT_FILENO, "interruptn", sizeof("interruptn"));
green_yield();
}
这就是我在yield函数中所做的:一个队列,我们从中获得下一个要运行的线程。问题是,在我在线程之间交换上下文之前的任何时候,我都可能得到中断。特别是因为我在这个函数的末尾交换了上下文。
int green_yield(){
green_t *susp = running ;
// add susp to ready queue
// ===========================
enQueue(ready_queue, susp);
// ===========================
// select the next thread for execution
// ===========================
green_t * next = deQueue(ready_queue);
running = next;
// ===========================
// save current state into susp->context and switch to next->context
// ===========================
swapcontext(susp->context, next->context);
return 0;}
我该怎么做才能确保我首先完成yield函数,然后得到中断?
前言:根据您的系统硬件,对stdout的write((系统调用可能需要超过10us的时间。因此,使用10us的循环计时器从SIGALRM处理程序调用它可能是错误的。
在GLIBC中,信号(SIGALRM,time_handler(等效于带有SA_RESTART标志的sigaction((SIGALRM信号在处理程序的执行过程中被阻塞。因此,在运行处理程序时不会收到信号。它在处理程序执行期间被隐式阻止,并在完成后被取消阻止。由于后者调用green_yield((,因此在green_yiel((内部运行时不会得到信号。
当getcontext((在SIGALRM未阻塞的情况下保存信号掩码时(我猜你在创建线程时在程序开始时会这样称呼它(,当你交换上下文以从一个运行信号处理程序的中断线程转到下一个可调度线程时,新运行的线程:
- 在第一个调度时间,从其getcontext(((线程创建点(返回。即使上一个线程没有从信号处理程序返回,这也会恢复信号掩码,因为上下文包含SIGALRM未阻塞的信号掩码。当计时器再次超时时,SIGALRM将再次出现以中断新运行的线程,该线程将在调用swapcontext((的信号处理程序中产生CPU。这一次,保存的上下文包含一个信号掩码,该掩码带有阻止的SIGALRM
- 在随后的调度时间,从swapcontext((返回,因为它被信号中断,因此正在运行信号处理程序的末尾。上下文恢复被阻止的SIGALRM信号,但这将作为信号处理程序执行的一部分被取消阻止,因为它的执行从信号处理程序的末尾重新开始
即使前面的方法应该有效,请注意,当发出信号时,系统会在当前进程堆栈的顶部创建一个堆栈帧,以使信号处理程序显示为用户程序调用的函数,并在中断点返回。堆栈上的此帧不能被从全局进程堆栈上的任何点运行的线程损坏。可以考虑使用sigaltstack(((请参阅下面的注释(。
你的线程实现怎么样?它们都共享同一个堆栈(进程堆栈(。创建它们时,它们都使用getcontext((保存上下文,几乎在全局流程堆栈的同一点。因此,当你从一个线程切换到另一个线程时,新运行的线程可能会破坏以前运行的线程的堆栈框架。。。我认为这是你应该关注的一点:安排你的线程,让它们在自己的全局堆栈区域中运行,或者使用makecontext((之类的东西在自己的堆栈中运行。后者的手册提供了一个创建具有独立堆栈的多个执行线程的示例。
旁注:
- swapcontext((不是信号处理程序中允许的函数调用的一部分:参见man 7信号安全。所以,从那里打电话是不安全的。但同时,我们可以看到,可以从信号处理程序安全地调用非本地gotos(即longjmp(((。由于swapcontext((看起来像一个非本地goto,因此在与longjmp((相同的条件下调用它可能是安全的
- sigaltstack((的手册提供了一些使用信号处理程序中的swapcontext((的提示