从已保存的上下文重新启动线程

时间:2011-08-11 11:45:15

标签: c linux

我正在尝试使用fork实现多线程应用程序的检查点方案。我会把检查站带到一个安全的地方,比如一道屏障。一个线程将调用fork来复制地址空间,并且信号将被发送到所有其他线程,以便它们可以保存它们的上下文并将其写入文件。

分叉进程最初不会运行。只有在需要从检查点重新启动时,才会向其发送一个信号,以便它可以开始运行。此时,将从已保存的上下文中重新创建未分叉但其上下文已保存的线程。

我的第一个问题是,是否足以从保存的上下文中重新创建线程并从那里运行它们,如果我假设没有锁定,在检查点期间没有待处理的信号......最后,如何创建一个线程以从已知的上下文运行。

1 个答案:

答案 0 :(得分:2)

如果没有与pthreads实现的主要集成,你想要的是不可能。内部线程结构可能包含自己的内核空间线程ID,这些线程在恢复的上下文中会有所不同。

听起来像你真正想要的是forkall,这是非常重要的。我不认为障碍对你想要完成的事情有用。异步中断和检查点与同步一样好。

如果您想尝试将forkall黑客攻击到glibc中,您应该首先查看NPTL用于在线程之间使用信号同步setxid调用的setuid()代码。同样的原则是实现forkall所需要的,但你基本上在信号处理程序中调用setjmp而不是setuid,然后在创建新的longjmp后再调用它们孩子的线程。之后,您必须修补线程结构以获得正确的pid / tid值,释放创建的多余新堆栈等。

编辑:由于glibc / NPTL中的setxid代码对于不熟悉代码库的人来说是相当密集的读取,因此您可能会查看我在musl中的相应代码,称为__synccall

http://git.etalabs.net/cgi-bin/gitweb.cgi?p=musl;a=blob;f=src/thread/synccall.c;h=91ac5eb77322da7393f778da29d35fb3c2def15d;hb=HEAD

它使用信号同步所有线程,然后逐个在每个线程中顺序运行回调。要实现forkall,你需要在fork之前执行类似的操作,但是除了回调之外,只需为每个线程保存跳转缓冲区,除了调用线程(你不能使用回调,因为返回将使您刚刚保存的跳转缓冲区无效),然后从调用线程执行fork。之后,您将生成N个新线程,并让它们跳回旧线程的已保存跳转缓冲区,并销毁它们的新(不需要的)堆栈。您还需要使用正确的系统调用来更新其线程寄存器(例如x86上的%gs)和tid地址。

然后你需要采用这些想法并将它们与glibc的线程分配和线程堆栈缓存框架集成。 : - )