对象的序列化:不涉及线程状态,对吧?

时间:2008-10-08 18:03:18

标签: java c++ multithreading serialization systemc

我正在努力研究将执行程序的状态存储到磁盘并将其重新引入的基本原则。在我们当前的设计中,每个对象(具有函数指针列表的C级事物,低级自制的面向对象 - 以及这样做的非常好的理由)将是调用将其显式状态导出为可写和可恢复的格式。使这项工作的关键属性是与对象相关的所有状态确实封装在对象数据结构中。

还有其他解决方案,您可以使用活动对象,其中有一些用户级线程附加到某些对象。因此,程序计数器,寄存器内容和堆栈内容突然成为程序状态的一部分。据我所知,没有什么好方法可以在任意时间点将这些东西序列化到磁盘上。线程必须将其自身停放在一些特殊状态,其中程序计数器等没有任何表示,因此基本上将它们的执行状态机状态“保存”到显式对象状态。

我查看了一系列序列化库,据我所知,这是一个通用属性。

核心问题是:或者这实际上不是这样吗?是否存在可以包含线程状态的保存/恢复解决方案,就线程执行的代码位置而言?

请注意,在虚拟机中保存整个系统状态不会计算,这不是真正序列化状态,而只是冻结计算机并移动它。这是一个显而易见的解决方案,但大部分时间都有点重量级。

有些问题清楚表明我在解释我们如何做事的想法时并不够清楚。我们正在开发一个模拟器系统,允许编写非常严格的内部代码运行规则。特别是,我们在对象构造和对象状态之间进行了完全划分。每次设置系统时都会重新创建接口函数指针,而不是状态的一部分。状态仅由特定的指定“属性”组成,每个属性具有定义的get / set函数,该函数在内部运行时表示和存储表示之间进行转换。对于对象之间的指针,它们都被转换为名称。所以在我们的设计中,一个对象可能会在存储中出现:

Object foo {
  value1: 0xff00ff00;
  value2: 0x00ffeedd;
  next_guy_in_chain: bar;
}

Object bar {
  next_guy_in_chain: null;
}

链接列表永远不会出现在模拟结构中,每个对象代表某种硬件单元。

问题是有些人想要这样做,但也有线程作为编码行为的方式。这里的“行为”实际上是模拟单元状态的变异。基本上,我们所设计的设计说,所有这些变化都必须在原子完整的操作中进行,这些操作被调用,完成它们的工作并返回。所有状态都存储在对象中。您有一个被动模型,或者它可以被称为“运行到完成”或“事件驱动”。

另一种思考方式是让对象有活动线程处理它们,它们以与传统Unix线程相同的方式处于永久循环中,并且永远不会终止。这是我试图看看它是否可以合理地存储到磁盘的情况,但是如果没有在下面插入VM,这似乎是不可行的。

更新,2009年10月:与此相关的论文于2009年在FDL会议上发布,请参阅this paper关于检查点和SystemC。

7 个答案:

答案 0 :(得分:2)

我不认为仅序列化程序的“某些线程”可以工作,因为你会遇到同步问题(这里描述了一些问题http://java.sun.com/j2se/1.3/docs/guide/misc/threadPrimitiveDeprecation.html)。 因此,坚持整个计划是获得一致状态的唯一可行方法。

您可能会关注的是正交持久性。有一些原型实现:

http://research.sun.com/forest/COM.Sun.Labs.Forest.doc.external_www.PJava.main.html

http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.17.7429

但是他们中的任何一个都不再被维护或者获得了很大的吸引力(afaik)。我认为检查点毕竟不是最好的解决方案。在我自己的项目http://www.siebengeisslein.org中,我正在尝试使用轻量级事务来调度事件,因此不必维护线程状态(因为在事务结束时,线程调用堆栈再次为空,如果是在事务中期停止操作,所有内容都回滚,因此线程调用堆也很重要。 你可能可以用任何OODBMS实现类似的东西。

另一种看待事物的方式是延续(http://en.wikipedia.org/wiki/Continuationhttp://jauvm.blogspot.com/)。它们是一种在已定义的代码位置暂停执行的方法(但它们不一定是持久化线程状态)。

我希望这会给你一些起点(但是没有现成的解决方案)。

编辑:阅读完您的说明后:您一定要查看OODBMS。在自己的事务中调度每个事件,而不关心线程。

答案 1 :(得分:1)

这听起来像是保存虚拟机的状态,并且能够以完全相同的方式恢复它,这正是您想要的。

如果您只需要能够使用与之前执行所使用的数据相同的数据启动程序,那么您只需要保存并恢复持久数据,每个线程的确切状态应该不重要,因为它会发生如此之快的变化 - 而下一次事物的实际地址会有所不同。无论如何,使用数据库应该可以提供这种能力。

答案 2 :(得分:1)

比尝试序列化程序状态更好的方法是使用数据检查点实现Crash Only Software。您如何进行数据检查点取决于您的实施和问题域。

答案 3 :(得分:0)

您不应该尝试序列化程序对磁盘的状态。因为你的程序永远不会完全控制其状态,除非操作系统允许它,在这种情况下......它是操作系统的一部分。

不能保证指向某个虚拟内存位置的指针会再次指向同一个虚拟内存位置(除了诸如heap-begin / end,stack-begin之类的属性),因为程序操作系统对虚拟内存的选择是不确定的。您通过sbrk从OS请求的页面或malloc等更高级别的接口将从任何地方开始。

更好:

  • 代码清理并检查您的设计:哪些州属性是其中的一部分?
  • 不要使用这种低级语言,因为创建你尝试做的事情的开销不值得。
  • 如果必须使用C,请考虑使生命尽可能简单的方法(考虑offsetof运算符和属性结构,如第一个成员从偏移量0开始)。

我怀疑您希望缩短序列化/反序列化特定数据结构所需的开发时间,例如链接列表。请放心,您尝试做的事情并非无足轻重,而且还需要做更多工作。如果您坚持这样做,请考虑查看操作系统的内存管理代码和操作系统的分页机制。 ; - )

编辑由于附加问题:您说的设计听起来像某种状态机;对象属性设置为可序列化,可以恢复函数指针。

首先,关于对象中的线程状态:这些只有在存在典型的并发编程问题(如竞争条件等)时才有意义。如果是这种情况,则需要线程同步功能,例如作为互斥体,信号量等。然后您可以随时访问属性以序列化/反序列化并保证安全。

其次,关于对象设置:看起来很酷,不确定你是否有二进制或其他对象表示。假设二进制文件:如果可以表示内存中的实际结构(这有点编码开销),您可以轻松地序列化它们。 在对象的开头插入某种类ID值,并有一个指向实际装备的查找表。查看第一个sizeof(id)字节,您就知道您拥有哪种结构。然后你就会知道那里有哪个结构。

在序列化/反序列化时,解决这样的问题:你可以查找假设打包的长度(成员之间没有间距)结构,分配该大小并一个接一个地读/写成员。想想偏移量,或者,如果您的编译器支持它,只需使用压缩结构。

编辑由于大胆的核心问题: - )不,没有;不是为了C。

答案 4 :(得分:0)

看起来你想在C ++中拥有closure。正如您所指出的那样,语言中没有内置机制可以让您这样做。据我所知,基本上不可能以完全一般的方式进行。通常,在没有VM的语言中很难做到。您可以通过执行类似于您已建议基本上创建一个维护执行环境/状态的闭包对象的方式来伪装它。然后在处于已知状态时将其序列化。

你的函数指针也会遇到麻烦。可以在每次加载时将这些函数加载到不同的内存地址。

答案 5 :(得分:0)

我认为线程状态是一个实现细节,可能不适合序列化。您希望保存对象的状态 - 不一定是它们的方式。

作为您希望采用此方法的示例,请考虑无中断升级。如果您正在运行应用程序的N版并希望升级到版本N + 1,则可以使用对象序列化来执行此操作。但是,“版本N + 1”线程与版本N线程不同。

答案 6 :(得分:0)

这样的东西实际上是在JSR 323中为Java提出的:

http://tech.puredanger.com/2008/01/09/strong-mobility-for-java/

但不被接受为过于理论化:

http://tech.puredanger.com/2008/01/24/jcp-votes-down-jsr-323/

如果您按照链接进行操作,可以找到一些有关此问题的有趣研究。