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

Pra*_*tic 7 c linux signals preemption multitasking

我想知道是否有可能在Linux上的用户空间中的单个进程中实现本机代码的抢占式多任务处理.(也就是说,从外部暂停一些正在运行的本机代码,保存上下文,在不同的上下文中交换,以及恢复执行,所有这些都由用户空间协调,但使用可能进入内核的调用.)我以为这可以使用信号来完成处理器SIGALRM和*context()家庭,但事实证明,整个*context()家庭是异步信号不安全的,所以不能保证方法的工作.我确实发现了一个实现这个想法的要点,所以显然它确实在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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

Bas*_*tch 5

您无法可靠地更改信号处理程序内的上下文。(如果您从某个信号处理程序中执行此操作,它通常会在实践中起作用,但并非总是如此,因此它是未定义的行为)。

您可以在信号处理程序中设置一些volatile sig_atomic_t标志(阅读有关内容)(请参阅signal(7)、signal-safety(7)、sigreturn(2) ...)并定期检查该标志(例如至少每几毫秒一次)你的代码,例如在大多数调用之前,或者在你的事件循环内(如果有的话)等等......所以它变成了协作的用户态调度。sig_atomic_t

如果您可以更改代码,例如当您设计一些发出 C 代码的编译器(常见做法),或者如果您破解 C 编译器以发出此类测试,那么事情会更容易。然后,您将更改代码生成器,以便有时在生成的代码中发出此类测试。

您可能希望禁止阻塞系统调用并将其替换为非阻塞变体或包装器。另请参见poll(2)、fcntl(2)和F_SETFL等O_NONBLOCK...

您可能希望代码生成器避免大型调用堆栈,例如像 GCC 的-fsplit-stack 检测选项那样(阅读有关GCC 中的splitstacks 的信息)。

如果您生成(或编写一些)汇编程序,则可以使用此类技巧。AFAIK Go 编译器对其 goroutine 使用类似的东西。研究您的ABI,例如从这里。

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

附言。如果您对使用类似技巧的垃圾收集技术感兴趣,请查看MTA 上的MPS和Cheney (例如Chicken Scheme)。

  • 值得补充的是,[glibc 确实提供了 POSIX 不需要的相关安全保证](https://www.gnu.org/software/libc/manual/html_node/System-V-contexts.html)(例如 `getcontext ()` 和 `makecontext()` 是 AS-安全的),但一些必要的函数仍然是 AS-不安全的。 (2认同)