可以在Linux上的用户空间中实现本机代码的抢占式多任务处理吗?

时间:2018-01-06 00:42:47

标签: c linux signals multitasking preemption

我想知道是否可以在Linux上的用户空间中的单个进程中实现本机代码的抢占式多任务处理。 (也就是说,从外部暂停一些正在运行的本机代码,保存上下文,在不同的上下文中交换,以及恢复执行,所有这些都由用户空间协调,但使用可能进入内核的调用。)我以为这可以使用信号来完成SIGALRM*context()系列的处理程序,但事实证明整个*context()系列都是async-signal-unsafe,因此无法确保此方法有效。我确实找到了一个实现这个想法的gist,所以显然它确实在Linux上运行,至少有时候,即使POSIX不需要工作。 gist将此作为SIGALRM上的信号处理程序安装,进行多次*context()次调用:

void
timer_interrupt(int j, siginfo_t *si, void *old_context)
{
    /* Create new scheduler context */
    getcontext(&signal_context);
    signal_context.uc_stack.ss_sp = signal_stack;
    signal_context.uc_stack.ss_size = STACKSIZE;
    signal_context.uc_stack.ss_flags = 0;
    sigemptyset(&signal_context.uc_sigmask);
    makecontext(&signal_context, scheduler, 1);

    /* save running thread, jump to scheduler */
    swapcontext(cur_context,&signal_context);
}

Linux是否提供使此方法正确的保证?有没有办法使这个正确吗?有没有完全不同的方法来正确地做到这一点?

(通过“在用户空间中实现”我并不是说我们永远不会进入内核。我的意思是与内核实现的抢占式多任务进行对比。)

1 个答案:

答案 0 :(得分:2)

您无法可靠地更改信号处理程序中的上下文。 (如果你是从某个信号处理程序那样做的,它通常通常在实践中工作,但并非总是如此,因此它是undefined behavior)。

您可以在信号处理程序中设置一些volatile sig_atomic_t标记(请参阅sig_atomic_t)(请参阅signal(7)signal-safety(7)sigreturn(2) ...)和在代码中检查标志(例如,每隔几毫秒),例如至少每隔几毫秒一次,例如在大多数呼叫之前,或者在event loop内,如果你有一个等等......那么它就变成了合作用户土地调度。

如果您可以更改代码,则更容易做到,例如当你设计一些发出C代码的编译器(一个common practice),或者你破解你的C编译器发出这样的测试。然后,您将更改代码生成器,以便有时在生成的代码中发出此类测试。

您可能希望禁止阻止系统调用,并将其替换为non-blocking变体或包装器。另请参阅poll(2)fcntl(2)F_SETFLO_NONBLOCK等...

您可能希望代码生成器避免使用大型调用堆栈,例如:像海湾合作委员会-fsplit-stack instrumentation option那样(在GCC中读到splitstacks)。

如果你生成(或写一些)汇编程序,你可以使用这些技巧。 AFAIK Go编译器对其goroutines使用类似的东西。研究你的ABI,例如来自here

但是,内核启动的抢占式调度更可取(在Linux上仍会在进程或内核任务之间发生,请参阅clone(2))。

PS。如果使用类似技巧的垃圾收集技术对您感兴趣,请查看MPSCheney on the MTA(例如转到Chicken Scheme)。