C++ dynamic_cast实现原理

时间:2022-08-31 23:58:15

dynamic_cast是一个操作符,其用法不再赘述。查看汇编码可以发现实际调用的是这个函数__RTDynamicCast,其内部实现如下:

rtti.h:

  1. #pragma once
  2. extern "C" {
  3. #include <windows.h>
  4. };
  5. typedef const type_info TypeDescriptor;
  6. struct PMD
  7. {
  8. ptrdiff_t mdisp; //vftable offset
  9. ptrdiff_t pdisp; //vftable offset
  10. ptrdiff_t vdisp; //vftable offset(for virtual base class)
  11. };
  12. typedef const struct _s_RTTIBaseClassDescriptor
  13. {
  14. TypeDescriptor                  *pTypeDescriptor;
  15. DWORD                           numContainedBases;
  16. PMD                             where;
  17. DWORD                           attributes;
  18. } _RTTIBaseClassDescriptor;
  19. typedef const struct  _s_RTTIBaseClassArray
  20. {
  21. _RTTIBaseClassDescriptor* arrayOfBaseClassDescriptors[3];
  22. }_RTTIBaseClassArray;
  23. typedef const struct _s_RTTIClassHierarchyDescriptor
  24. {
  25. DWORD                           signature;
  26. DWORD                           attributes;
  27. DWORD                           numBaseClasses;
  28. _RTTIBaseClassArray             *pBaseClassArray;
  29. }_RTTIClassHierarchyDescriptor;
  30. typedef const struct _s_RTTICompleteObjectLocator
  31. {
  32. DWORD                           signature;
  33. DWORD                           offset;          //vftbl相对this的偏移
  34. DWORD                           cdOffset;        //constructor displacement
  35. TypeDescriptor                  *pTypeDescriptor;
  36. _RTTIClassHierarchyDescriptor   *pClassDescriptor;
  37. }_RTTICompleteObjectLocator;
  38. #define BCD_NOTVISIBLE              0x00000001
  39. #define BCD_AMBIGUOUS               0x00000002
  40. #define BCD_PRIVORPROTINCOMPOBJ     0x00000004
  41. #define BCD_PRIVORPROTBASE          0x00000008
  42. #define BCD_VBOFCONTOBJ             0x00000010
  43. #define BCD_NONPOLYMORPHIC          0x00000020
  44. #define BCD_PTD(bcd)                ((bcd).pTypeDescriptor)
  45. #define BCD_NUMCONTBASES(bcd)       ((bcd).numContainedBases)
  46. #define BCD_WHERE(bcd)              ((bcd).where)
  47. #define BCD_ATTRIBUTES(bcd)         ((bcd).attributes)
  48. #define CHD_MULTINH                 0x00000001 //多重继承
  49. #define CHD_VIRTINH                 0x00000002 //虚拟继承
  50. #define CHD_AMBIGUOUS               0x00000004 //有重复基类的多重继承
  51. #define CHD_SIGNATURE(chd)          ((chd).signature)
  52. #define CHD_ATTRIBUTES(chd)         ((chd).attributes)
  53. #define CHD_NUMBASES(chd)           ((chd).numBaseClasses)
  54. #define CHD_PBCA(chd)               ((chd).pBaseClassArray)
  55. #define COL_SIGNATURE(col)          ((col).signature)
  56. #define COL_OFFSET(col)             ((col).offset)
  57. #define COL_CDOFFSET(col)           ((col).cdOffset)
  58. #define COL_PTD(col)                ((col).pTypeDescriptor)
  59. #define COL_PCHD(col)               ((col).pClassDescriptor)
  60. extern "C" PVOID __cdecl __RTDynamicCast (PVOID, LONG, PVOID, PVOID, BOOL);
  61. extern "C" PVOID __cdecl __RTtypeid (PVOID);     // ptr to vfptr
  62. #define TYPEIDS_EQ(pID1, pID2)  ((pID1 == pID2) || !strcmp(pID1->name(), pID2->name()))

rtti.cpp:

  1. #include <stdio.h>
  2. #include <typeinfo>
  3. #include "rtti.h"
  4. #pragma warning(disable:4297)
  5. static PVOID __cdecl FindCompleteObject(PVOID *);
  6. static _RTTIBaseClassDescriptor * __cdecl FindSITargetTypeInstance(PVOID,_RTTICompleteObjectLocator *,TypeDescriptor *,int,TypeDescriptor *);
  7. static _RTTIBaseClassDescriptor * __cdecl FindMITargetTypeInstance(PVOID,_RTTICompleteObjectLocator *,TypeDescriptor *,int,TypeDescriptor *);
  8. static _RTTIBaseClassDescriptor * __cdecl FindVITargetTypeInstance(PVOID,_RTTICompleteObjectLocator *,TypeDescriptor *,int,TypeDescriptor *);
  9. static ptrdiff_t __cdecl PMDtoOffset(PVOID pThis, const PMD& pmd);
  10. extern "C" PVOID __cdecl __RTtypeid (PVOID inptr)
  11. {
  12. if (!inptr) {
  13. throw std::bad_typeid ("Attempted a typeid of NULL pointer!");
  14. return NULL;
  15. }
  16. __try {
  17. // Ptr to CompleteObjectLocator should be stored at vfptr[-1]
  18. _RTTICompleteObjectLocator *pCompleteLocator = (_RTTICompleteObjectLocator *) ((*((void***)inptr))[-1]);
  19. return (PVOID) pCompleteLocator->pTypeDescriptor;
  20. }
  21. __except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER:             EXCEPTION_CONTINUE_SEARCH)
  22. {
  23. throw std::__non_rtti_object ("Access violation - no RTTI data!");
  24. }
  25. }
  26. extern "C" PVOID __cdecl __RTDynamicCast (
  27. PVOID inptr,         // Pointer to polymorphic object
  28. LONG VfDelta,       // Offset of vfptr in object
  29. PVOID SrcType,      // Static type of object pointed to by inptr
  30. PVOID TargetType,   // Desired result of cast
  31. BOOL isReference)   // TRUE if input is reference, FALSE if input is ptr
  32. {
  33. PVOID pResult;
  34. _RTTIBaseClassDescriptor *pBaseClass;
  35. if (inptr == NULL)
  36. return NULL;
  37. __try {
  38. PVOID pCompleteObject = FindCompleteObject((PVOID *)inptr);
  39. _RTTICompleteObjectLocator *pCompleteLocator = (_RTTICompleteObjectLocator *) ((*((void***)inptr))[-1]);
  40. // Adjust by vfptr displacement, if any
  41. inptr = (PVOID *) ((char *)inptr - VfDelta);
  42. // Calculate offset of source object in complete object
  43. int inptr_delta = (char *)inptr - (char *)pCompleteObject;
  44. if (!(CHD_ATTRIBUTES(*COL_PCHD(*pCompleteLocator)) & CHD_MULTINH)) {             // if not multiple inheritance
  45. pBaseClass = FindSITargetTypeInstance(pCompleteObject,
  46. pCompleteLocator,
  47. (TypeDescriptor *) SrcType,
  48. inptr_delta,
  49. (TypeDescriptor *) TargetType);
  50. } else if (!(CHD_ATTRIBUTES(*COL_PCHD(*pCompleteLocator)) & CHD_VIRTINH)) { // if multiple, but not virtual, inheritance
  51. pBaseClass = FindMITargetTypeInstance(pCompleteObject,
  52. pCompleteLocator,
  53. (TypeDescriptor *) SrcType,
  54. inptr_delta,
  55. (TypeDescriptor *) TargetType);
  56. } else {                                                                   // if virtual inheritance
  57. pBaseClass = FindVITargetTypeInstance(pCompleteObject,
  58. pCompleteLocator,
  59. (TypeDescriptor *) SrcType,
  60. inptr_delta,
  61. (TypeDescriptor *) TargetType);
  62. }
  63. if (pBaseClass != NULL) {
  64. // Calculate ptr to result base class from pBaseClass->where
  65. pResult = ((char *) pCompleteObject) + PMDtoOffset(pCompleteObject, pBaseClass->where);
  66. }else {
  67. pResult = NULL;
  68. if (isReference) {
  69. throw std::bad_cast("Bad dynamic_cast!");
  70. }
  71. }
  72. }
  73. __except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER: EXCEPTION_CONTINUE_SEARCH) {
  74. pResult = NULL;
  75. throw std::__non_rtti_object ("Access violation - no RTTI data!");
  76. }
  77. return pResult;
  78. }
  79. /////////////////////////////////////////////////////////////////////////////
  80. //
  81. // FindCompleteObject - Calculate member offset from PMD & this
  82. //
  83. // Output: pointer to the complete object containing class *inptr
  84. //
  85. // Side-effects: NONE.
  86. //
  87. static PVOID __cdecl FindCompleteObject (PVOID *inptr)          // Pointer to polymorphic object
  88. {
  89. // Ptr to CompleteObjectLocator should be stored at vfptr[-1]
  90. _RTTICompleteObjectLocator *pCompleteLocator = (_RTTICompleteObjectLocator *) ((*((void***)inptr))[-1]);
  91. char *pCompleteObject = (char *)inptr - pCompleteLocator->offset;
  92. // Adjust by construction displacement, if any
  93. if (pCompleteLocator->cdOffset)
  94. pCompleteObject += *(ptrdiff_t *)((char *)inptr - pCompleteLocator->cdOffset);
  95. return (PVOID) pCompleteObject;
  96. }
  97. static _RTTIBaseClassDescriptor * __cdecl FindSITargetTypeInstance (
  98. PVOID pCompleteObject,                          // pointer to complete object
  99. _RTTICompleteObjectLocator *pCOLocator, // pointer to Locator of complete object
  100. TypeDescriptor *pSrcTypeID,        // pointer to TypeDescriptor of source object
  101. int SrcOffset,                                          // offset of source object in complete object
  102. TypeDescriptor *pTargetTypeID)     // pointer to TypeDescriptor of result of cast
  103. {
  104. _RTTIBaseClassDescriptor *pBase;
  105. _RTTIBaseClassDescriptor * const *pBasePtr;
  106. DWORD i;
  107. for (i = 0, pBasePtr = pCOLocator->pClassDescriptor->pBaseClassArray->arrayOfBaseClassDescriptors;
  108. i < pCOLocator->pClassDescriptor->numBaseClasses;
  109. i++, pBasePtr++) {
  110. // Test type of selected base class
  111. pBase = *pBasePtr;
  112. if (TYPEIDS_EQ(pBase->pTypeDescriptor, pTargetTypeID) &&
  113. !(BCD_ATTRIBUTES(*pBase) & BCD_NOTVISIBLE)) {
  114. return pBase;
  115. }
  116. }
  117. return NULL;
  118. }
  119. static _RTTIBaseClassDescriptor * __cdecl FindMITargetTypeInstance (
  120. PVOID pCompleteObject,                          // pointer to complete object
  121. _RTTICompleteObjectLocator *pCOLocator, // pointer to Locator of complete object
  122. TypeDescriptor *pSrcTypeID,        // pointer to TypeDescriptor of source object
  123. int SrcOffset,                                          // offset of source object in complete object
  124. TypeDescriptor *pTargetTypeID)     // pointer to TypeDescriptor of result of cast
  125. {
  126. _RTTIBaseClassDescriptor *pBase, *pSubBase;
  127. _RTTIBaseClassDescriptor * const *pBasePtr, * const *pSubBasePtr;
  128. DWORD i, j;
  129. // First, try down-casts
  130. for (i = 0, pBasePtr = pCOLocator->pClassDescriptor->pBaseClassArray->arrayOfBaseClassDescriptors;
  131. i < pCOLocator->pClassDescriptor->numBaseClasses;
  132. i++, pBasePtr++) {
  133. pBase = *pBasePtr;
  134. // Test type of selected base class
  135. if (TYPEIDS_EQ(pBase->pTypeDescriptor, pTargetTypeID)) {
  136. // If base class is proper type, see if it contains our instance of source class
  137. for (j = 0, pSubBasePtr = pBasePtr+1;
  138. j < pBase->numContainedBases;
  139. j++, pSubBasePtr++) {
  140. pSubBase = *pSubBasePtr;
  141. if (TYPEIDS_EQ(pSubBase->pTypeDescriptor, pSrcTypeID) &&
  142. (PMDtoOffset(pCompleteObject, pSubBase->where) == SrcOffset)) {
  143. // Yes, this is the proper instance of source class
  144. return pBase;
  145. }
  146. }
  147. }
  148. }
  149. // Down-cast failed, try cross-cast
  150. for (i = 0, pBasePtr = pCOLocator->pClassDescriptor->pBaseClassArray->arrayOfBaseClassDescriptors;
  151. i < pCOLocator->pClassDescriptor->numBaseClasses;
  152. i++, pBasePtr++) {
  153. pBase = *pBasePtr;
  154. // Check if base class has proper type, is accessible & is unambiguous
  155. if (TYPEIDS_EQ(pBase->pTypeDescriptor, pTargetTypeID) &&
  156. !(BCD_ATTRIBUTES(*pBase) & BCD_NOTVISIBLE) &&
  157. !(BCD_ATTRIBUTES(*pBase) & BCD_AMBIGUOUS)) {
  158. return pBase;
  159. }
  160. }
  161. return NULL;
  162. }
  163. static _RTTIBaseClassDescriptor * __cdecl FindVITargetTypeInstance (
  164. PVOID pCompleteObject,                          // pointer to complete object
  165. _RTTICompleteObjectLocator *pCOLocator, // pointer to Locator of complete object
  166. TypeDescriptor *pSrcTypeID,        // pointer to TypeDescriptor of source object
  167. int SrcOffset,                                          // offset of source object in complete object
  168. TypeDescriptor *pTargetTypeID)     // pointer to TypeDescriptor of result of cast
  169. {
  170. _RTTIBaseClassDescriptor *pBase, *pSubBase;
  171. _RTTIBaseClassDescriptor * const *pBasePtr, * const *pSubBasePtr;
  172. _RTTIBaseClassDescriptor *pResult = NULL;
  173. DWORD i, j;
  174. // First, try down-casts
  175. for (i = 0, pBasePtr = pCOLocator->pClassDescriptor->pBaseClassArray->arrayOfBaseClassDescriptors;
  176. i < pCOLocator->pClassDescriptor->numBaseClasses;
  177. i++, pBasePtr++) {
  178. pBase = *pBasePtr;
  179. // Test type of selected base class
  180. if (TYPEIDS_EQ(pBase->pTypeDescriptor, pTargetTypeID)) {
  181. // If base class is proper type, see if it contains our instance of source class
  182. for (j = 0, pSubBasePtr = pBasePtr+1;
  183. j < pBase->numContainedBases;
  184. j++, pSubBasePtr++) {
  185. pSubBase = *pSubBasePtr;
  186. if (TYPEIDS_EQ(pSubBase->pTypeDescriptor, pSrcTypeID) &&
  187. (PMDtoOffset(pCompleteObject, pSubBase->where) == SrcOffset)) {
  188. // Yes, this is the proper instance of source class - make sure it is unambiguous
  189. // Ambiguity now determined by inequality of offsets of source class within complete object, not pointer inequality
  190. if ((pResult != NULL) && (PMDtoOffset(pCompleteObject, pResult->where) != PMDtoOffset(pCompleteObject, pBase->where))) {
  191. // We already found an earlier instance, hence ambiguity
  192. return NULL;
  193. }
  194. else {
  195. // Unambiguous
  196. pResult = pBase;
  197. }
  198. }
  199. }
  200. }
  201. }
  202. if (pResult != NULL)
  203. return pResult;
  204. // Down-cast failed, try cross-cast
  205. for (i = 0, pBasePtr = pCOLocator->pClassDescriptor->pBaseClassArray->arrayOfBaseClassDescriptors;
  206. i < pCOLocator->pClassDescriptor->numBaseClasses;
  207. i++, pBasePtr++) {
  208. pBase = *pBasePtr;
  209. // Check if base class has proper type, is accessible & is unambiguous
  210. if (TYPEIDS_EQ(pBase->pTypeDescriptor, pTargetTypeID) &&
  211. !(BCD_ATTRIBUTES(*pBase) & BCD_NOTVISIBLE) &&
  212. !(BCD_ATTRIBUTES(*pBase) & BCD_AMBIGUOUS)) {
  213. return pBase;
  214. }
  215. }
  216. return NULL;
  217. }
  218. static ptrdiff_t __cdecl PMDtoOffset(
  219. PVOID pThis,                    // ptr to complete object
  220. const PMD& pmd)                 // pointer-to-member-data structure
  221. {
  222. ptrdiff_t RetOff = 0;
  223. if (pmd.pdisp >= 0) {                       // if base is in the virtual part of class
  224. RetOff = pmd.pdisp;
  225. RetOff += *(ptrdiff_t*)((char*)*(ptrdiff_t*)((char*)pThis + RetOff) + pmd.vdisp);
  226. }
  227. RetOff += pmd.mdisp;
  228. return RetOff;
  229. }

测试代码:

  1. // WinDemo.cpp : 定义控制台应用程序的入口点。
  2. //
  3. #include "stdafx.h"
  4. #include <iostream>
  5. #include "rtti.h"
  6. using namespace std;
  7. class A
  8. {
  9. public:
  10. virtual void func()
  11. {
  12. cout << "A::func()" << endl;
  13. }
  14. };
  15. class B : public A
  16. {
  17. public:
  18. virtual void func()
  19. {
  20. cout << "B::func()" << endl;
  21. }
  22. };
  23. class C : public A
  24. {
  25. public:
  26. virtual void func()
  27. {
  28. cout << "C::func()" << endl;
  29. }
  30. private:
  31. int _val;
  32. };
  33. int main(int argc, char* argv[])
  34. {
  35. A* pa = new C;
  36. TypeDescriptor* ptypeA = &typeid(A);
  37. TypeDescriptor* ptypeC = &typeid(C);
  38. C* pc = (C*)__RTDynamicCast(pa, 0, (LPVOID)ptypeA, (LPVOID)ptypeC, FALSE);
  39. cout << pc << endl;
  40. return 0;
  41. }

从以上代码可以看出:只能在有虚函数的类层次之间使用dynamic_cast。要实现dynamic_cast,编译器会在每个含有虚函数的类的虚函数表的前四个字节存放一个指向_RTTICompleteObjectLocator结构的指针,当然还要额外空间存放_RTTICompleteObjectLocator及其相关结构的数据。以上面代码的类C来说:

C++ dynamic_cast实现原理

这个_RTTICompleteObjectLocator就是实现dynamic_cast的关键结构。里面存放了vfptr相对this指针的偏移,构造函数偏移(针对虚拟继承),type_info指针,以及类层次结构中其它类的相关信息。如果是多重继承,这些信息更加复杂。

所以,dynamic_cast的时间和空间代价是相对较高的,在设计时应避免使用。

如果整个工程都不需要dynamic_cast,可以禁用运行时类型信息(vs2008默认是启用的),这样编译器就不会产生_RTTICompleteObjectLocator及相关数据。

禁用方法如下:

依次选择【工程属性】、【配置属性】、【C/C++】、【语言】。将【启用运行时类型信息】改为”否“。

http://blog.csdn.net/passion_wu128/article/details/38511957

C++ dynamic_cast实现原理的更多相关文章

  1. 跟vczh看实例学编译原理——一:Tinymoe的设计哲学

    自从<序>胡扯了快一个月之后,终于迎来了正片.之所以系列文章叫<看实例学编译原理>,是因为整个系列会通过带大家一步一步实现Tinymoe的过程,来介绍编译原理的一些知识点. 但 ...

  2. 【Cocos2d-x 3&period;x】 场景切换生命周期、背景音乐播放和场景切换原理与源码分析

    大部分游戏里有很多个场景,场景之间需要切换,有时候切换的时候会进行背景音乐的播放和停止,因此对这块内容进行了总结. 场景切换生命周期 场景切换用到的函数: bool Setting::init() { ...

  3. C&plus;&plus; 多态的实现原理与内存模型

    多态在C++中是一个重要的概念,通过虚函数机制实现了在程序运行时根据调用对象来判断具体调用哪一个函数. 具体来说就是:父类类别的指针(或者引用)指向其子类的实例,然后通过父类的指针(或者引用)调用实际 ...

  4. cocos2d-x触摸分发器原理

    屏幕捕捉到触摸消息的派发流程: 如果有一个组件如果想要接收触摸事件,会通过继承一个CCTouchDelegate接口注册给CCTouchDispatcher,CCTouchDispatcher 中维护 ...

  5. c&plus;&plus;中多态性、dynamic&lowbar;cast、父类指针、父类对象、子类指针、子类对象

    c++多态性是依靠虚函数和父类指针指向子类对象来实现的.简单来说,父类中定义虚函数,父类指针指向子类对象,父类指针调用函数时调用的就是子类的函数. 父类没有定义虚函数,父类指针指向子类对象时,父类指针 ...

  6. Qt核心机制与原理

    转:  https://blog.csdn.net/light_in_dark/article/details/64125085 ★了解Qt和C++的关系 ★掌握Qt的信号/槽机制的原理和使用方法 ★ ...

  7. C&plus;&plus;中static&lowbar;cast和dynamic&lowbar;cast强制类型转换

    在C++标准中,提供了关于类型层次转换中的两个关键字static_cast和dynamic_cast. 一.static_cast关键字(编译时类型检查) 用法:static_cast < ty ...

  8. C&plus;&plus;中reinterpret&lowbar;cast、const&lowbar;cast、static&lowbar;cast、dynamic&lowbar;cast的作用与区别

    1.reinterpret_cast 作用及原理:将一个类型的指针,转换为另一个类型的指针,这种转换不用修改指针变量值数据存放格式(不改变指针变量值),只需在编译时重新解释指针的类型就可以,当然他也可 ...

  9. C&sol;C&plus;&plus;杂记:运行时类型识别(RTTI)与动态类型转换原理

    运行时类型识别(RTTI)的引入有三个作用: 配合typeid操作符的实现: 实现异常处理中catch的匹配过程: 实现动态类型转换dynamic_cast. 1. typeid操作符的实现 1.1. ...

随机推荐

  1. node&period;js图片上传

    1.node-formidable 对文件上传提供帮助的组件 2.app.js var formidable = require('formidable'); var http = require( ...

  2. Hadoop&period;2&period;x&lowbar;集群初建

    一.部分概念 1. 分布式:一个项目分为多个模块共同完成一个或多个任务,可部署在一个或多个机器 2. 集群:多个机器运行同一个项目或服务 3. 集群上可能运行着零个或多个分布式系统(比如Hadoop, ...

  3. paip&period;Java Annotation注解的作用and 使用

    paip.Java Annotation注解的作用and 使用 作者Attilax 艾龙,  EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:http://blog. ...

  4. Webx常用接口

    最近在学Webx框架, 在了解webx的工作流程后, 必须要会使用自带的接口和类 常用的 Navigator  这个接口中只有两种类型的方法, 及重定向与转发, 一般用在screen包下的类(注意:s ...

  5. 更改xcode上iphone模拟器颜色的方法--备用

    到模拟器的目录下修改图片即可——在Finder中显示,显示模拟器包内容,修改Contents/Resources/frame.png图片!

  6. 转:用&ZeroWidthSpace;C&ZeroWidthSpace;语&ZeroWidthSpace;言&ZeroWidthSpace;的&ZeroWidthSpace;r&ZeroWidthSpace;a&ZeroWidthSpace;n&ZeroWidthSpace;d&ZeroWidthSpace;&lpar;&ZeroWidthSpace;&rpar;&ZeroWidthSpace;和&ZeroWidthSpace;s&ZeroWidthSpace;r&ZeroWidthSpace;a&ZeroWidthSpace;n&ZeroWidthSpace;d&ZeroWidthSpace;&lpar;&ZeroWidthSpace;&rpar;&ZeroWidthSpace;产&ZeroWidthSpace;生&ZeroWidthSpace;伪&ZeroWidthSpace;随&ZeroWidthSpace;机&ZeroWidthSpace;数&ZeroWidthSpace;的&ZeroWidthSpace;方&ZeroWidthSpace;法&ZeroWidthSpace;总&ZeroWidthSpace;结

    标准库<cstdlib>(被包含于<iostream>中)提供两个帮助生成伪随机数的函数: 函数一:int rand(void): 从srand (seed)中指定的seed开 ...

  7. &lbrack;转&rsqb;Xcode的快捷键及代码格式化

    Xcode比较常用的快捷键,特别是红色标注的,很常用.1. 文件CMD + N: 新文件CMD + SHIFT + N: 新项目CMD + O: 打开CMD + S: 保存CMD+OPt+S:保存所有 ...

  8. animation,transform属性

    animation属性 使用@keyfarmes属性开启动画步骤 结构体:@keyfarmes name{ from{ } to{ } } @keyfarmes name{ 0%{ } 50%{ } ...

  9. Linux的启动流程 (二)

    引:本文以RedHat9.0和i386平台为例,剖析了从用户打开电源直到屏幕出现命令行提示符的整个Linux启动过程.并且介绍了启动中涉及到的各种文件.阅读Linux源代码,无疑是深入学习Linux的 ...

  10. JAVA执行远端服务器的脚本

    JAVA执行远端服务器的脚本 问题描述 实现思路 技术要点 代码实现 问题描述 工作中遇到这样一个问题,我们的应用为了实现高可用会采取双机部署,拓扑图大致如下: 这种方案可以简单的保证高可用,即便应用 ...