shm.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. /*
  2. * Copyright 2020 ByteDance Games, Inc. All Rights Reserved.
  3. *
  4. * Author: Liuziming <liuziming.kelvin@bytedance.com>
  5. */
  6. #pragma once
  7. #include <string>
  8. #include <vector>
  9. #include <list>
  10. #include <set>
  11. #include <map>
  12. #include <unordered_set>
  13. #include <unordered_map>
  14. #include "vptr_manager.h"
  15. #include "shm_helper.h"
  16. #include <stdint.h>
  17. // Containers redefined for shm from `std` namespace.
  18. namespace bg
  19. {
  20. using ShmString = std::basic_string<char, std::char_traits<char>, detail::ShmAllocator<char>>;
  21. template<typename T>
  22. using ShmVector = std::vector<T, detail::ShmAllocator<T>>;
  23. template<typename T>
  24. using ShmList = std::list<T, detail::ShmAllocator<T>>;
  25. template<typename Key, typename Compare = std::less<Key>>
  26. using ShmSet = std::set<Key, Compare, detail::ShmAllocator<Key>>;
  27. template<typename Key, typename Value, typename Compare = std::less<Key>>
  28. using ShmMap = std::map<Key, Value, Compare, detail::ShmAllocator<std::pair<const Key, Value>>>;
  29. template<typename Key, typename Hash = std::hash<Key>, typename Pred = std::equal_to<Key>>
  30. using ShmUnorderedSet = std::unordered_set<Key, Hash, Pred, detail::ShmAllocator<Key>>;
  31. template<typename Key, typename Value, typename Hash = std::hash<Key>, typename Pred = std::equal_to<Key>>
  32. using ShmUnorderedMap = std::unordered_map<Key, Value, Hash, Pred, detail::ShmAllocator<std::pair<const Key, Value>>>;
  33. } // namespace bg
  34. // C-style memory related functions for shm.
  35. namespace bg
  36. {
  37. /**
  38. * 分配一块共享内存块
  39. * @param bytes 要分配的共享内存块的大小
  40. * @return 分配的共享内存块的首地址,如果分配失败,返回NULL
  41. */
  42. void* ShmMalloc(size_t bytes);
  43. /**
  44. * 调整已分配的共享内存块的大小
  45. * @param old_ptr 要调整的共享内存块的首地址
  46. * @param new_bytes 要调整的新的大小
  47. * @return 调整后的共享内存块的首地址
  48. * @note 调用本函数分以下几种情况:
  49. * 1. 如果old_ptr为NULL,则该调用等同于ShmMalloc(new_bytes)
  50. * 2. 如果new_bytes为0,则该调用等同于ShmFree(old_ptr),并返回NULL
  51. * 3. 其它情况,视new_bytes大小决定:
  52. * - 原共享内存块大小足够并且不触发内存缩减操作时,直接返回原共享内存块
  53. * - 原共享内存块大小不足或者new_bytes远小于原共享内存块大小时,调用ShmMalloc分配新共享内
  54. * 存块,将原内存的内容使用memcpy拷贝到新内存,然后调用ShmFree释放原共享内存块并返回新共
  55. * 享内存块。如果ShmMalloc分配失败,则不释放原内存并返回NULL。注意,如果new_bytes小于原内
  56. * 存大小,则在调用memcpy时会产生内容截断。
  57. */
  58. void* ShmRealloc(void* old_ptr, size_t new_bytes);
  59. /**
  60. * 分配n块连续的共享内存块并填充0
  61. * @param n 要分配的共享内存块的数量
  62. * @param bytes 要分配的共享内存块的大小
  63. * @return 分配的已填充0的连续的共享内存块的首地址,如果分配失败,返回NULL
  64. */
  65. void* ShmCalloc(size_t n, size_t bytes);
  66. /**
  67. * 释放传入的共享内存块
  68. * @param ptr 要释放的共享内存块的首地址
  69. */
  70. void ShmFree(void* ptr);
  71. } // namespace bg
  72. // Helper functions.
  73. namespace bg
  74. {
  75. namespace detail
  76. {
  77. template<typename T, typename... Args>
  78. T* ShmNew(std::true_type /* objects with vptr(s) */, Args&& ... args)
  79. {
  80. return VptrManager::NewVptrObject<T>(std::forward<Args>(args)...);
  81. }
  82. template<typename T, typename... Args>
  83. T* ShmNewExtraSpace(std::true_type /* objects with vptr(s) */, size_t extra_space, Args&& ... args)
  84. {
  85. return VptrManager::NewVptrObjectExtraSpace<T>(extra_space, std::forward<Args>(args)...);
  86. }
  87. template<typename T, typename... Args>
  88. T* ShmNew(std::false_type /* objects without vptr(s) */, Args&& ... args)
  89. {
  90. void* ptr = ShmMalloc(sizeof(T));
  91. if(SHM_LIKELY(ptr))
  92. {
  93. new(ptr) T(std::forward<Args>(args)...);
  94. }
  95. return static_cast<T*>(ptr);
  96. }
  97. template<typename T, typename... Args>
  98. T* ShmNewExtraSpace(std::false_type /* objects without vptr(s) */, size_t extra_space, Args&& ... args)
  99. {
  100. void* ptr = ShmMalloc(sizeof(T) + extra_space);
  101. if(SHM_LIKELY(ptr))
  102. {
  103. new(ptr) T(std::forward<Args>(args)...);
  104. }
  105. return static_cast<T*>(ptr);
  106. }
  107. template<typename T>
  108. void ShmDelete(std::true_type /* objects with vptr(s) */, T* ptr)
  109. {
  110. VptrManager::DelVptrObject(ptr);
  111. }
  112. template<typename T>
  113. void ShmDelete(std::false_type /* objects without vptr(s) */, T* ptr)
  114. {
  115. if(SHM_LIKELY(ptr))
  116. {
  117. ptr->~T();
  118. ShmFree(ptr);
  119. }
  120. }
  121. } // namespace detail
  122. } // namespace bg
  123. // C++-style memory related functions for shm.
  124. namespace bg
  125. {
  126. /**
  127. * 构造一个保存在共享内存中的类的实例
  128. * @tparam T 要保存在共享内存中的类的类型
  129. * @tparam Args T类型的构造函数的参数类型
  130. * @param args T类型的构造函数的参数
  131. * @return 指向T类型实例的指针,如果分配失败,返回NULL
  132. */
  133. template<typename T, typename... Args>
  134. T* ShmNew(Args&&... args)
  135. {
  136. return detail::ShmNew<T>(detail::HasVptr<T>(), std::forward<Args>(args)...);
  137. }
  138. /**
  139. * 构造一个保存在共享内存中的类的实例,并且分配额外的内存(额外的内存紧临该实例)
  140. * @tparam T 要保存在共享内存中的类的类型
  141. * @tparam Args T类型的构造函数的参数类型
  142. * @param extra_space 要额外分配的内存大小
  143. * @param args T类型的构造函数的参数
  144. * @return 指向T类型实例的指针,如果分配失败,返回NULL
  145. */
  146. template<typename T, typename... Args>
  147. T* ShmNewExtraSpace(size_t extra_space, Args&&... args)
  148. {
  149. return detail::ShmNewExtraSpace<T>(detail::HasVptr<T>(), extra_space, std::forward<Args>(args)...);
  150. }
  151. /**
  152. * 析构一个保存在共享内存中的类的实例
  153. * @tparam T 保存在共享内存中的类的类型
  154. * @param ptr T类型实例的指针
  155. */
  156. template<typename T>
  157. void ShmDelete(T* ptr)
  158. {
  159. detail::ShmDelete(detail::HasVptr<T>(), ptr);
  160. }
  161. } // namespace bg
  162. // Singleton related functions.
  163. namespace bg
  164. {
  165. /**
  166. * 获得一个保存在共享内存中的单例
  167. * @tparam T 要获取的单例类型
  168. * @tparam Args T类型的构造函数的参数类型
  169. * @param args T类型的构造函数的参数
  170. * @return 指向T类型单例的指针,如果分配失败,返回NULL
  171. * @note T类型不能是多态类型,否则编译失败
  172. */
  173. template<typename T, typename... Args>
  174. T* ShmGetSingleton(Args&& ... args)
  175. {
  176. static_assert(!detail::HasVptr<T>::value, "singleton does not support polymorphic types.");
  177. detail::TypeName type(typeid(T).name(), sizeof(T));
  178. bool first_call = false;
  179. void* ptr = detail::ShmGetSingleton(type, sizeof(T), &first_call);
  180. if(ptr && first_call)
  181. {
  182. new(ptr) T(std::forward<Args>(args)...);
  183. }
  184. return static_cast<T*>(ptr);
  185. }
  186. /**
  187. * 销毁一个保存在共享内存中的单例
  188. * @tparam T 单例类的类型
  189. * @note T类型不能是多态类型,否则编译失败
  190. */
  191. template<typename T>
  192. void ShmDeleteSingleton()
  193. {
  194. static_assert(!detail::HasVptr<T>::value, "singleton does not support polymorphic types.");
  195. detail::TypeName type(typeid(T).name(), sizeof(T));
  196. if(!detail::ShmHasSingleton(type))
  197. {
  198. return;
  199. }
  200. void* ptr = detail::ShmGetSingleton(type, sizeof(T), nullptr);
  201. SHM_ASSERT_RETURN_VOID(ptr);
  202. static_cast<T*>(ptr)->~T();
  203. detail::ShmFreeSingleton(type);
  204. }
  205. } // namespace bg
  206. // Initialize/Finalize functions.
  207. namespace bg
  208. {
  209. /**
  210. * 初始化整个Shm模块时的一些可配置选项
  211. */
  212. struct ShmOptions
  213. {
  214. // 是否以resume模式初始化Shm模块
  215. bool resume;
  216. // 是否要替换系统内存分配器,目前没有实现,但是依然强烈不建议打开此选项
  217. bool replace_sys_alloc;
  218. // 是否在ShmInit里面去修复虚表指针,默认值为true,如果有dlopen打开so,并且so
  219. // 里面有虚表指针需要修复的话,建议此选项为false,同时,此时需要调用者自己在
  220. // dlopen之后去调用修复虚表指针的函数
  221. bool fix_vptr_on_init;
  222. // Shm模块的日志输出函数,如果为NULL,则所有日志默认输出到stdout
  223. ShmLogger* logger;
  224. // Shm模块管理器挂载的内存地址,默认值为0x600000000000,通常情况下不需要自定
  225. // 义。如果要自定义该值,请避开进程中已映射的区域(可查看/proc/<pid>/maps),并
  226. // 尽量选用较空的位置,不然整个Shm模块将无法工作
  227. uintptr_t main_address;
  228. // 每块原始共享内存的映射大小,也是每块原始共享内存的最大大小,默认值为4GB,通
  229. // 常情况下不需要自定义
  230. size_t shm_block_mmap_size;
  231. // 在原始共享内存不足时增长的大小,默认值是32MB,通常情况下不需要自定义
  232. size_t shm_block_grow_size;
  233. // 原始共享内存可挂载的内存地址的数量,默认值为16(即fixed_addresses的大小)
  234. size_t fixed_address_count;
  235. // Shm模块的标识符,由于共享内存全系统可见,为防止冲突,请务必保证每个进程有
  236. // 独一无二的标识符,并且在进程重启后保证标识符不变
  237. char identifier[64];
  238. // 原始共享内存可挂载的内存地址列表,默认有16个地址,通常情况下这些地址不需要
  239. // 自定义。如果要自定义,请参考main_address的自定义方式,并且保证从每个地址开
  240. // 始,到加上shm_block_grow_size结束的区域不会和别的内存映射区域重叠
  241. uintptr_t fixed_addresses[16];
  242. /**
  243. * 配置项构造函数,设置传入的选项,并将其它选项设置为默认值
  244. * @param resume 设置是否是resume模式
  245. * @param identifier 设置模块标识符,不能超过64字节
  246. * @param logger 设置日志输出函数,可以为NULL
  247. */
  248. ShmOptions(bool resume, const char* identifier, ShmLogger* logger);
  249. bool AddFixedAddress(uintptr_t addr);
  250. bool PopFixedAddress(uintptr_t* addr);
  251. static size_t FillDefaultFixedAddresses(uintptr_t* result, size_t max_count);
  252. };
  253. /**
  254. * 初始化整个Shm模块
  255. * @param options 用于初始化Shm模块的一些配置项
  256. * @return 成功返回true,失败返回false,并输出相应的错误日志
  257. * @note 在使用任何本模块的内存操作函数前,必须调用本函数进程初始化
  258. */
  259. bool ShmInit(const ShmOptions& options);
  260. /**
  261. * 销毁整个Shm模块
  262. * @note 本函数会销毁整个Shm模块,以及共享内存中的数据,因此,只有在
  263. * 正常停服时才需要调用本函数,进行热更新或者进程崩溃等需要重启进
  264. * 程并使用原来共享内存的情况下,请务必*不要*调用本函数
  265. */
  266. void ShmFini();
  267. /**
  268. * 修复在共享内存中实例的虚表指针
  269. * @note 通常情况下,虚表指针会在ShmInit中进行修复,但有些程序会用
  270. * dlopen打开so,而如果dlopen发生在ShmInit之后,则ShmInit没办法修
  271. * 复so中的虚表指针,此时,需要调用者在dlopen之后显式调用该函数进
  272. * 行虚表指针修复
  273. */
  274. void ShmFixVptr();
  275. } // namespace bg
  276. // A dirty hack on `std::hash` for `bg::ShmString`.
  277. namespace std
  278. {
  279. template<>
  280. struct hash<bg::ShmString>
  281. {
  282. size_t operator()(const bg::ShmString& str) const noexcept
  283. {
  284. return std::_Fnv1a_append_value(str.length(), str.data());
  285. }
  286. };
  287. } // namespace std