我正在寻找一种方法来重新启动一个线程,无论是从该线程的上下文内部还是从线程外部,可能是从另一个进程内部。 (这些选项中的任何一个都可以工作。)我知道休眠整个过程的难度,而且我很确定那些相同的困难会影响线程。但是,无论如何,我都希望有人有一些见解。
我的目标是暂停,保存到文件,并从其确切的上下文重新启动正在运行的线程,而不修改该线程的代码,或者更确切地说,仅在一个小区域中进行修改 - 即,我不能编写序列化函数整个代码。主代码块必须是未修改的,并且不会有任何全局/系统句柄(文件句柄,套接字,互斥体等)。不需要保存像CPU寄存器这样的简单细节。但基本上应该保存堆,堆栈和程序计数器,以及使其从保存点再次正确运行逻辑所需的任何其他内容。如果保存与否,程序的结果状态应该没有区别。
这是针对高可靠性软件的调试程序;目标是使用各种脚本运行软件模拟输入,并能够暂停正在运行的模拟,然后再重新启动它 - 或者将SIM卡转移到分支点,保存,制作大量副本然后再运行从共同的起点模拟。这就是主程序无法修改的原因。
主线程语言是用C ++编写的,应该在Windows和Linux上运行,但是如果只能在一个系统上执行此操作,那么这也是可以接受的。
提前致谢。
答案 0 :(得分:3)
我认为你所要求的要比你想象的要复杂得多。我对Windows编程不太熟悉,但这里有一些你在Linux中遇到的困难。
保存的线程只能从最初生成线程的根进程中恢复,否则动态库将被破坏。因为这种对磁盘的保存基本上没有意义。原因是动态库每次加载时都会加载到不同的地址。解决这个问题的唯一方法是完全控制动态链接,这不是一件小事。这是可能的,但非常可怕。
挂起的线程将在堆中包含变量。你需要能够通过线程找到所有全局'拥有'。无法确定堆中任何一块的“拥有”状态。将来可能使用C ++ 0x的垃圾收集ABI。你不能只假设整个堆栈属于要暂停的线程。主线程在创建线程时使用堆。因此,在反序列化暂停的线程时吹走堆会破坏主线程。
您需要解决全局问题。而不仅仅是在线程中创建的全局变量。 Globals(或静态)可以并且通常在动态库中创建。
程序有更多资源而不仅仅是内存。您有文件句柄,网络套接字,数据库连接等。文件句柄只是一个数字。如果没有打开文件的进程的上下文,序列化其内存是完全没有意义的。
所有这一切。我不认为核心问题是不可能的,只是你应该考虑采用不同的方法。
无论如何要尝试实现这个,暂停的线程需要处于已知状态。我想要停止的线程会调用一个库函数意味着停止进程以便它可以恢复。
我认为linux系统调用fork是你的朋友。 Fork完美地复制了一个过程。让系统运行到所需的点和叉。一个叉子等待分叉其他人。第二个fork运行一组输入。
一旦完成第一个fork就可以了。同样,第二个fork可以运行另一组输入。
无限期地继续。
答案 1 :(得分:2)
线程在进程的上下文中运行。因此,如果您想要执行任何操作,例如将线程状态保存到磁盘,则需要“休眠”整个过程。
您需要序列化整个流程数据集。并且您需要存储当前的线程执行点。我认为序列化过程是可行的(检查出boost :: serialize)但是线程停止点要困难得多。我会把它放在可以通过代码停止的地方,但正如你所说,你无法修改代码。
鉴于这个问题,您正在考虑虚拟化应用程序运行的平台,并使用其暂停功能暂停整个事情。您可以在虚拟化供应商的功能中找到有关如何执行此操作的更多信息,例如Xen。
答案 2 :(得分:0)
由于程序的整个逻辑地址空间是线程上下文的一部分,因此您必须对整个过程进行休眠。
如果可以保证线程只使用局部变量,则可以保存其堆栈。用pthreads很容易挂起一个线程,但我不知道你怎么能从外面访问它的堆栈。
答案 3 :(得分:0)
您必须这样做的方法是通过虚拟机快照;获取VMWare Workstation的副本,然后您可以编写代码以自动在不同点启动/停止/快照机器。任何其他方法都是站不住脚的,因为虽然你可能能够冻结并重新处理一个进程,但是你无法重建它所期望的系统状态(Caspin提到的所有东西都像文件句柄一样)。