协程调度场景下,如何在信号处理程序中原子设置sigmask并执行long jump?
解决协程调度器中swapcontext非原子操作引发的信号重入问题
1. 利用内核默认信号阻塞机制
默认情况下,信号处理程序执行时内核会自动阻塞当前触发的信号(比如你的SIGURG),除非你在sigaction中设置了SA_NODEFER标志。如果没有使用该标志,理论上信号处理程序不会重入,但swapcontext的掩码修改步骤仍有风险:若目标协程的上下文掩码允许SIGURG,那么在swapcontext修改掩码后、跳转前的窗口内,SIGURG会处于未阻塞状态,可能触发新的信号处理。
应对方式:
- 注册SIGURG时,不要设置
SA_NODEFER,依赖内核自动阻塞信号的机制。 - 所有协程的上下文信号掩码默认阻塞SIGURG,仅当协程处于可被抢占的运行状态时,才由调度器主动解除SIGURG的阻塞。
2. 手动实现原子化的掩码切换与上下文跳转
绕过swapcontext的非原子操作,手动控制信号掩码和上下文切换流程:
- 在信号处理程序入口,先调用
sigprocmask显式阻塞SIGURG(双重保险)。 - 使用
getcontext保存当前线程的上下文。 - 确保目标上下文的信号掩码包含SIGURG阻塞。
- 调用
setcontext直接切换到目标上下文,避免swapcontext的中间非原子步骤。
注意:
setcontext不会保存当前上下文,需提前用getcontext完成保存操作。
3. 将调度逻辑移出信号处理程序
信号处理程序的执行环境受限,尽量减少其中的复杂操作:
- 在信号处理程序中仅设置一个全局的"需要调度"标志,并用
siglongjmp跳转到主线程的调度循环入口。 - 主线程循环检查该标志,当标志被触发时,再执行协程调度和上下文切换操作。
这种方式从根本上避免了信号重入风险,代价是调度延迟略高(取决于主线程检查标志的频率)。
核心代码示例
#include <ucontext.h> #include <signal.h> #include <stdbool.h> #include <stdlib.h> ucontext_t main_ctx, coro_ctx; bool need_schedule = false; void sigurg_handler(int sig) { // 显式阻塞SIGURG,双重保险 sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGURG); sigprocmask(SIG_BLOCK, &mask, NULL); // 保存当前上下文 getcontext(&main_ctx); need_schedule = true; // 跳转到主线程调度循环 siglongjmp(main_ctx.uc_mcontext, 1); } void coroutine_func() { while (1) { // 协程业务逻辑示例 for (int i = 0; i < 1000000; i++); // 主动让出CPU时,解除SIGURG阻塞允许下次抢占 sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGURG); sigprocmask(SIG_UNBLOCK, &mask, NULL); // 切换回主线程调度 setcontext(&main_ctx); } } int main() { // 初始化协程上下文,默认阻塞SIGURG getcontext(&coro_ctx); coro_ctx.uc_stack.ss_sp = malloc(8192); coro_ctx.uc_stack.ss_size = 8192; sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGURG); coro_ctx.uc_sigmask = mask; makecontext(&coro_ctx, coroutine_func, 0); // 注册SIGURG信号,不使用SA_NODEFER struct sigaction sa; sa.sa_handler = sigurg_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGURG, &sa, NULL); // 主线程调度循环 while (1) { if (sigsetjmp(main_ctx.uc_mcontext, 1) == 1) { need_schedule = false; // 切换到协程 setcontext(&coro_ctx); } // 主线程其他逻辑 } }
内容的提问来源于stack exchange,提问作者Markity
相关产品推荐
相关产品推荐

