程序代码模块热替换

这是一篇几年前就想写的一篇文章,初始于最初学习Erlang的时候。初
步的研究是在研究Java的ClassLoader机制。

但这也是一篇没有经过严格验证的文章,由于操作系统实现的复杂性,
没有经过完整的确认。

因此,下面的内容仅是理论的推定。

程序代码的热替换非常的麻烦,真实的环境需要考虑比如代码正在执行
中,需要进行热替换,这会丢失非常多的信息而难以进行。因此,这里做
了一些的约定(其实,Erlang要实现代码的热替换也相同的有一些限定)。

  1. 代码中的不使用全局变量或者静态变量
  2. 这一约定用来简化程序代码热替换时实现的复杂量。这样可以将程序使
    用的所有的数据和代码独立出来,当进行代码替换时,保证数据还是原来
    的数据。至于对数据内容或者格式的更新,在替换的代码中实现。这一步
    限定了使用dll的方式,至于实现的机制架构,BREW系统就是一个可行性的
    例子。

  3. 被替换的代码只有在不运行的时候才允许被替换
  4. 这一约定会将热替换的使用稍微复杂些,但简化了实现机制,Erlang的
    代码的热替换也是这样的约定。这样一来,对于线程之类的,当线程在运
    行的时候,需要等线程结束才可以进行代码的替换,对于使用者而言,需
    要在监测到模块代码文件更新的时候重启线程的调用,甚至保存部分的结
    果之类的。因此,在调用的时候需要约定要相应机制的接口。

在满足上述的两个条件下,大致的实现如下:

主程序做最简单的事情,在监测线程中不断的监测模块文件的时间戳信息。
当监测到时间信息不一致的时候,准备进行模块的热替换。如果模块没有
加载,则加载模块,并调用约定要的初始化函数,初始化函数将会将模块
分配的数据保存在主程序中。如果模块已经被加载,则首先卸载掉模块
(在卸载函数中,由模块自己通知其他的卸载调用,并告之卸载缘由),
由于约定1的存在,数据依然是保留的,然后加载新的模块,并调用更新数
据的函数(更新数据函数通知其他部分自己的更新)。如果模块已经卸载,
则重新加载。主程序退出的时候,卸载所有的模块(在卸载函数中,由模块自己通知其他的卸载调用,并告之卸载缘由)

在上述的流程中,一个非常关键的内容是接口约定时的顺序以及参数,上述实现
大概约定的公用接口为4-6个不等,但在模块内部分管理自己的接口的时候,
需要对自己的函数表做一个非常严格的维护,在代码进行替换前,需要和
其他模块,尤其是依赖模块进行沟通,不然的话会危及整个系统的稳定性。

理论上而言,dll加载到内存中的时候,磁盘上的文件是可以进行替换
的,但这个操作要看操作系统了,比如windows默认就不可以,会提示说文
件正在被使用,也有可能会出现由于这个dll被多次加载而无法完成正确的
替换调用。而对于我自己在嵌入式中做的动态加载机制,大都没有实现的如此
复杂,上述的机制是可以实现的。

所以,还是Erlang使用的方便,不用再重新造轮子了。。。

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据