tc_malloc_chunk.h 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716
  1. #ifndef __TC_MALLOC_CHUNK_H
  2. #define __TC_MALLOC_CHUNK_H
  3. #include <iostream>
  4. #include <cassert>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <vector>
  8. using namespace std;
  9. namespace tars
  10. {
  11. /////////////////////////////////////////////////
  12. /**
  13. * @file tc_mem_chunk.h
  14. * @brief 内存分配器
  15. *
  16. */
  17. static const size_t kPageShift = 15;
  18. static const size_t kNumClasses = 78;
  19. static const size_t kPageSize = 1 << kPageShift;
  20. static const size_t kMaxSize = 256 * 1024;
  21. static const size_t kAlignment = 8;
  22. static const size_t kMaxPages = 1 << (20 - kPageShift);
  23. static const size_t kPageAlignment = 64;
  24. ////////////////////////////////////////////////////////////////////////////////////////////////////
  25. /**
  26. * 请求的内存大小与请求实际得到的内存大小的映射
  27. * 实际得到的内存大小与内存大小所属的大小类别的映射
  28. * 内存大小所属的大小类别与该内存大小类别需要的内存页的映射
  29. */
  30. class SizeMap
  31. {
  32. public:
  33. SizeMap() { Init(); }
  34. /*
  35. *size所属的尺寸类别
  36. */
  37. inline int SizeClass(int size)
  38. {
  39. return class_array_[ClassIndex(size)];
  40. }
  41. /*
  42. *尺寸类别cl对应的尺寸大小
  43. */
  44. inline size_t ByteSizeForClass(size_t cl)
  45. {
  46. return class_to_size_[cl];
  47. }
  48. /*
  49. *尺寸类别cl对应的页数
  50. */
  51. inline size_t class_to_pages(size_t cl)
  52. {
  53. return class_to_pages_[cl];
  54. }
  55. private:
  56. static inline size_t ClassIndex(int s)
  57. {
  58. const bool big = (s > kMaxSmallSize);
  59. const size_t add_amount= big ? (127 + (120<<7)) : 7;
  60. const size_t shift_amount = big ? 7 : 3;
  61. return (s + add_amount) >> shift_amount;
  62. }
  63. static inline int LgFloor(size_t n)
  64. {
  65. int log = 0;
  66. for (int i = 4; i >= 0; --i)
  67. {
  68. int shift = (1 << i);
  69. size_t x = n >> shift;
  70. if (x != 0)
  71. {
  72. n = x;
  73. log += shift;
  74. }
  75. }
  76. return log;
  77. }
  78. /*
  79. *初始化
  80. */
  81. void Init();
  82. size_t AlignmentForSize(size_t size);
  83. int NumMoveSize(size_t size);
  84. private:
  85. static const int kMaxSmallSize = 1024;
  86. static const size_t kClassArraySize = ((kMaxSize + 127 + (120 << 7)) >> 7) + 1;
  87. size_t class_to_size_[kNumClasses];
  88. size_t class_to_pages_[kNumClasses];
  89. unsigned char class_array_[kClassArraySize];
  90. };
  91. ////////////////////////////////////////////////////////////////////////////////////////////////////
  92. class Static
  93. {
  94. public:
  95. static SizeMap* sizemap()
  96. {
  97. return &_sizemap;
  98. }
  99. private:
  100. static SizeMap _sizemap;
  101. };
  102. ////////////////////////////////////////////////////////////////////////////////////////////////////
  103. /**
  104. * TC_Span内存分配器
  105. *
  106. * 用于分配sizeof(TC_Span)大小的内存块
  107. */
  108. class TC_SpanAllocator
  109. {
  110. public:
  111. TC_SpanAllocator() : _pHead(NULL), _pData(NULL) {}
  112. void* getHead() const { return _pHead; }
  113. /**
  114. * 初始化
  115. * @param pAddr, 地址, 换到应用程序的绝对地址
  116. * @param mem_size, 内存大小
  117. */
  118. void create(void* pAddr, size_t iSpanSize, size_t iSpanCount);
  119. /**
  120. * 连接上
  121. * @param pAddr, 地址, 换到应用程序的绝对地址
  122. */
  123. void connect(void* pAddr);
  124. /**
  125. * 重建
  126. */
  127. void rebuild();
  128. /**
  129. * 是否还有可用block
  130. *
  131. * @return bool
  132. */
  133. bool isSpanAvailable() const { return _pHead->_spanAvailable > 0; }
  134. /**
  135. * 分配一个区块
  136. *
  137. * @return void*
  138. */
  139. void* allocate();
  140. /**
  141. * 释放区块
  142. * @param pAddr
  143. */
  144. void deallocate(void *pAddr);
  145. #pragma pack(1)
  146. struct tagSpanAlloc
  147. {
  148. size_t _iSpanSize; /**span区块大小*/
  149. size_t _iSpanCount; /**span个数*/
  150. size_t _firstAvailableSpan; /**第一个可用的span索引*/
  151. size_t _spanAvailable; /**可用span个数*/
  152. };
  153. #pragma pack()
  154. static size_t getHeadSize() { return sizeof(tagSpanAlloc); }
  155. protected:
  156. /**
  157. * 初始化
  158. */
  159. void init(void *pAddr);
  160. //禁止copy构造
  161. TC_SpanAllocator(const TC_SpanAllocator &mcm);
  162. //禁止复制
  163. TC_SpanAllocator &operator=(const TC_SpanAllocator &mcm);
  164. private:
  165. /**
  166. * 区块头指针
  167. */
  168. tagSpanAlloc *_pHead;
  169. /**
  170. * 数据区指针
  171. */
  172. unsigned char *_pData;
  173. };
  174. ////////////////////////////////////////////////////////////////////////////////////////////////////
  175. /**
  176. * 内存分配器
  177. *
  178. * 将连续的内存按32K大小分页
  179. * 并通过TC_Span和PageMap进行管理
  180. */
  181. class TC_Page
  182. {
  183. public:
  184. #pragma pack(1)
  185. struct tagShmFlag
  186. {
  187. bool _bShmProtectedArea; /*是否设置保护区*/
  188. int _iShmFlag; /*内存空间标识,内存未使用前为0,使用后为1*/
  189. size_t _iShmMemSize; /*内存空间大小*/
  190. size_t _iShmSpanAddr; /*TC_Span内存区的起始地址,相对地址*/
  191. size_t _iShmPageMapAddr; /*PageMap内存区的起始地址,相对地址*/
  192. size_t _iShmPageAddr; /*Data区的内存区的其实地址,相对地址*/
  193. size_t _iShmSpanNum; /*TC_Span内存区中span的个数*/
  194. size_t _iShmPageMapNum; /*PageMap内存区中map的个数*/
  195. size_t _iShmPageNum; /*Data内存区中页的个数*/
  196. };
  197. struct tagModifyData
  198. {
  199. size_t _iModifyAddr; /*修改的地址*/
  200. char _cBytes; /*字节数*/
  201. size_t _iModifyValue; /*值*/
  202. };
  203. struct tagModifyHead
  204. {
  205. char _cModifyStatus; /*修改状态: 0:目前没有人修改, 1: 开始准备修改, 2:修改完毕, 没有copy到内存中*/
  206. size_t _iNowIndex; /*更新到目前的索引*/
  207. tagModifyData _stModifyData[32]; /*一次最多32次修改*/
  208. };
  209. struct TC_Span
  210. {
  211. size_t start; /*span所管理的页内存的起始页号*/
  212. size_t length; /*页的个数*/
  213. size_t next;
  214. size_t prev;
  215. size_t objects; /*用于分配内存*/
  216. unsigned int refcount; /*按照sizeclass类大小划分span拥有的内存后的个数*/
  217. unsigned int sizeclass; /*内存大小的类别*/
  218. unsigned int location; /*用于识别该span是空闲,还是处于使用中*/
  219. enum { IN_USE, ON_FREELIST };
  220. };
  221. struct TC_CenterList
  222. {
  223. size_t size_class; /*内存大小的类别*/
  224. TC_Span empty; /*空闲链表*/
  225. TC_Span nonempty; /*非空闲链表*/
  226. };
  227. #pragma pack()
  228. public:
  229. TC_Page() : _pShmFlagHead(NULL),_pModifyHead(NULL),_pCenterCache(NULL),_pLarge(NULL),_pFree(NULL),_pSpanMemHead(NULL),_pPageMap(NULL),_pData(NULL) {}
  230. /**
  231. * 初始化
  232. * @param pAddr, 地址, 换到应用程序的绝对地址
  233. * @param iShmMemSize, 内存大小
  234. * @param bProtectedArea, 是否使用保护区
  235. */
  236. void create(void *pAddr, size_t iShmMemSize, bool bProtectedArea);
  237. /**
  238. * 连接上
  239. * @param pAddr, 地址, 换到应用程序的绝对地址
  240. */
  241. void connect(void *pAddr);
  242. /**
  243. * 重建
  244. */
  245. void rebuild();
  246. /**
  247. * 分配一个区块
  248. * @param iClassSize,需要分配的内存大小类别
  249. * @param iAllocSize, 分配的数据块大小
  250. * @param iPageId, 该内存所属的TC_Span的起始页号
  251. * @param iIndex, 该内存所属的TC_Span的按iAllocSize大小划分后的第iIndex个
  252. * @return void *
  253. */
  254. void* fetchFromSpansSafe(size_t iClassSize, size_t &iAllocSize, size_t &iPageId, size_t &iIndex);
  255. /**
  256. * 释放内存, 根据该内存所属TC_Span的起始页和该TC_Span按大小类别划分后的第iIndex个
  257. * @param iPageId, 该内存所属的TC_Span的起始页号
  258. * @param iIndex, 该内存所属的TC_Span的按iAllocSize大小划分后的第iIndex个
  259. */
  260. void releaseToSpans(size_t iPageId, size_t iIndex);
  261. /**
  262. * 释放内存, 绝对地址
  263. * @param pAddr
  264. */
  265. void releaseToSpans(void* pObject);
  266. /**
  267. * 根据该内存所属TC_Span的起始页和该TC_Span按大小类别划分后的第iIndex个得到该内存块的起始绝对地址
  268. * @param iPageId, 该内存所属的TC_Span的起始页号
  269. * @param iIndex, 该内存所属的TC_Span的按iAllocSize大小划分后的第iIndex个
  270. */
  271. void* getAbsolute(size_t iPageId, size_t iIndex);
  272. /**
  273. * 修改更新到内存中
  274. */
  275. void doUpdate(bool bUpdate = false);
  276. /**
  277. * 获得用于存放数据的页内存的数量
  278. */
  279. inline size_t getPageNumber();
  280. /**
  281. * 获得用于存放数据的页内存的大小
  282. */
  283. inline size_t getPageMemSize();
  284. /**
  285. * 获得TC_Page所管理内存的结束位置
  286. */
  287. inline size_t getPageMemEnd();
  288. /**
  289. * 传给TC_Page的内存的最小大小
  290. */
  291. static size_t getMinMemSize()
  292. {
  293. return sizeof(tagShmFlag) + sizeof(tagModifyHead) + sizeof(TC_CenterList) * kNumClasses + sizeof(TC_Span) + sizeof(TC_Span) * kMaxPages + TC_SpanAllocator::getHeadSize() + sizeof(TC_Span) + sizeof(size_t) + kPageSize;
  294. }
  295. protected:
  296. //禁止copy构造
  297. TC_Page(const TC_Page &mcm);
  298. //禁止复制
  299. TC_Page &operator=(const TC_Page &mcm);
  300. /**
  301. * 初始化
  302. */
  303. void init(void *pAddr);
  304. /**
  305. * 按页初始化用于存放数据的内存
  306. */
  307. void initPage(void *pAddr);
  308. /////////////////////////////////////////////////////////////////////////////////////////////////
  309. /**
  310. * 初始化list双向链表
  311. */
  312. inline void DLL_Init(TC_Span* list, size_t iIndex);
  313. /**
  314. * 从双向链表中删除span指向的节点
  315. */
  316. inline void DLL_Remove(TC_Span* span);
  317. /**
  318. * 将span指向的节点加入到双向链表list中
  319. */
  320. inline void DLL_Prepend(TC_Span* list, TC_Span* span);
  321. /**
  322. * 双向链表list是否为空
  323. */
  324. inline bool DLL_IsEmpty(TC_Span* list, size_t iIndex)
  325. {
  326. return list->next == iIndex;
  327. }
  328. //////////////////////////////////////////////////////////////////////////////////////////////////
  329. /**
  330. * 得到PageMap中第k个元素存放的指针值,其实际意义就是看Data内存区中第k块内存页属于哪个TC_Span管理
  331. */
  332. size_t Get(size_t k) const
  333. {
  334. return _pPageMap[k];
  335. }
  336. /**
  337. * 设置PageMap中第k个元素存放的指针值,其实际意义就是看Data内存区中第k块内存页属于哪个TC_Span管理
  338. */
  339. void Set(size_t k, size_t v)
  340. {
  341. _pPageMap[k] = v;
  342. }
  343. /**
  344. * 确保n未超过页内存的数目与k之差
  345. */
  346. bool Ensure(size_t k, size_t n)
  347. {
  348. return n <= _pShmFlagHead->_iShmPageMapNum -k;
  349. }
  350. //////////////////////////////////////////////////////////////////////////////////////////////////
  351. /**
  352. * create或rebuild的时候会调用,用于初始化时将所有的内存页映射到_pLarge上
  353. */
  354. bool UseShmMem();
  355. /**
  356. * 得到内存页pPageId属于哪个TC_Span管理
  357. */
  358. TC_Span* GetDescriptor(size_t pPageId);
  359. /**
  360. * 在_pFree或_pLarge链表中查找n块连续的内存页,通过TC_Span返回
  361. */
  362. TC_Span* SearchFreeAndLargeLists(size_t n);
  363. /**
  364. * 分配n块连续的内存页,通过TC_Span返回,里面会调用SearchFreeAndLargeLists函数
  365. */
  366. TC_Span* New(size_t n);
  367. /**
  368. * 在_pLarge链表中分配n块连续的内存页,通过TC_Span返回
  369. */
  370. TC_Span* AllocLarge(size_t n);
  371. /**
  372. * 在span所管理的内存页中,分配出n块连续内存
  373. */
  374. TC_Span* Carve(TC_Span* span, size_t n);
  375. /**
  376. * 根据span所管理的内存页的数目,将该span插入到_pLarge或_pFree链表中
  377. */
  378. void PrependToFreeList(TC_Span* span);
  379. /**
  380. * 将span从它所属的链表中删除,里面调用MergeIntoFreeList
  381. */
  382. void Delete(TC_Span* span);
  383. /**
  384. * 将span从它所属的链表中删除,并且查看该span所管理的内存页前后连续的内存页是否空闲,若是,则进行合并操作
  385. */
  386. void MergeIntoFreeList(TC_Span* span);
  387. /**
  388. * 设置span所管理的内存页将要进行分割的内存大小类别
  389. */
  390. void RegisterSizeClass(TC_Span* span, size_t sc);
  391. /**
  392. * 将span所属的内存页,映射到_pPageMap中
  393. */
  394. void RecordSpan(TC_Span* span);
  395. //////////////////////////////////////////////////////////////////////////////////////////////////
  396. /**
  397. * 分配一个区块
  398. * @param iClassSize,需要分配的内存大小类别
  399. * @param iAllocSize, 分配的数据块大小
  400. * @param iPageId, 该内存所属的TC_Span的起始页号
  401. * @param iIndex, 该内存所属的TC_Span的按iAllocSize大小划分后的第iIndex个
  402. * @return void *
  403. */
  404. void* FetchFromSpans(size_t iClassSize, size_t &iAllocSize, size_t &iPageId, size_t &iIndex);
  405. /**
  406. * 按iClassSize类别的内存大小分割内存页
  407. */
  408. int Populate(size_t iClassSize);
  409. /**
  410. * 修改具体的值
  411. * @param iModifyAddr
  412. * @param iModifyValue
  413. */
  414. //inline void update(void* iModifyAddr, size_t iModifyValue);
  415. template<typename T>
  416. void update(void* iModifyAddr, T iModifyValue)
  417. {
  418. _pModifyHead->_cModifyStatus = 1;
  419. _pModifyHead->_stModifyData[_pModifyHead->_iNowIndex]._iModifyAddr = reinterpret_cast<size_t>(iModifyAddr) - reinterpret_cast<size_t>(_pShmFlagHead);
  420. _pModifyHead->_stModifyData[_pModifyHead->_iNowIndex]._iModifyValue = iModifyValue;
  421. _pModifyHead->_stModifyData[_pModifyHead->_iNowIndex]._cBytes = sizeof(iModifyValue);
  422. _pModifyHead->_iNowIndex++;
  423. assert(_pModifyHead->_iNowIndex < sizeof(_pModifyHead->_stModifyData) / sizeof(tagModifyData));
  424. }
  425. private:
  426. /**
  427. * 头部内存块头指针
  428. */
  429. tagShmFlag *_pShmFlagHead;
  430. /**
  431. * 保护区内存块头指针
  432. */
  433. tagModifyHead *_pModifyHead;
  434. /**
  435. * 中央自由链表头指针
  436. */
  437. TC_CenterList *_pCenterCache;
  438. /**
  439. * 大块内存页的链表指针(大于等于1MB内存块的链表)
  440. */
  441. TC_Span *_pLarge;
  442. /**
  443. * 小块内存页的链表头指针(小于1MB的内存块)
  444. */
  445. TC_Span *_pFree;
  446. /**
  447. * 用于分配TC_Span的内存区域的头指针
  448. */
  449. TC_SpanAllocator *_pSpanMemHead;
  450. /**
  451. * 与数据区内存相映射的内存区头指针
  452. */
  453. //void **_pPageMap;
  454. size_t *_pPageMap;
  455. /**
  456. * 数据区的头指针
  457. */
  458. void *_pData;
  459. /**
  460. * 用于分配TC_Span的内存分配器
  461. */
  462. TC_SpanAllocator _spanAlloc;
  463. };
  464. ////////////////////////////////////////////////////////////////////////////////////////////////////
  465. class TC_MallocChunkAllocator
  466. {
  467. public:
  468. TC_MallocChunkAllocator():_pHead(NULL),_pChunk(NULL),_nallocator(NULL) {}
  469. ~TC_MallocChunkAllocator()
  470. {
  471. clear();
  472. }
  473. void clear()
  474. {
  475. if(_nallocator)
  476. {
  477. delete _nallocator;
  478. _nallocator = NULL;
  479. }
  480. _pHead = NULL;
  481. _pChunk = NULL;
  482. }
  483. /**
  484. * 初始化
  485. * @param pAddr, 地址, 换到应用程序的绝对地址
  486. * @param iSize, 内存大小
  487. * @param bProtectedArea, 是否使用保护区,默认使用
  488. */
  489. void create(void *pAddr, size_t iSize, bool bProtectedArea = true);
  490. /**
  491. * 连接上
  492. * @param pAddr, 地址, 换到应用程序的绝对地址
  493. */
  494. void connect(void *pAddr);
  495. /**
  496. * 扩展空间
  497. *
  498. * @param pAddr, 已经是空间被扩展之后的地址
  499. * @param iSize
  500. */
  501. void append(void *pAddr, size_t iSize);
  502. void* getHead() const { return _pHead; }
  503. /**
  504. * 总计内存大小, 包括后续增加的内存块的大小
  505. *
  506. * @return size_t
  507. */
  508. size_t getMemSize() const { return _pHead->_iTotalSize; }
  509. /**
  510. * 用于存放数据的总计内存大小, 包括后续增加的内存块的大小
  511. *
  512. */
  513. size_t getAllCapacity();
  514. /**
  515. * 每个chunk的大小, 包括后续增加的内存块的大小
  516. *
  517. * vector<size_t>
  518. */
  519. void getSingleCapacity(vector<size_t> &vec_capacity);
  520. /**
  521. * 根据该内存所属TC_Span的起始页和该TC_Span按大小类别划分后的第iIndex个,换算成绝对地址
  522. * @param iPageId
  523. * @param iIndex
  524. * @return void*
  525. */
  526. void* getAbsolute(size_t iPageId, size_t iIndex);
  527. /**
  528. * 分配一个区块,绝对地址
  529. *
  530. * @param iNeedSize,需要分配的大小
  531. * @param iAllocSize, 分配的数据块大小
  532. * @return void*
  533. */
  534. void* allocate(size_t iNeedSize, size_t &iAllocSize);
  535. /**
  536. * 释放区块, 绝对地址
  537. * @param pAddr
  538. */
  539. void deallocate(void* pAddr);
  540. /**
  541. * 分配一个区块
  542. * @param iNeedSize,需要分配的大小
  543. * @param iAllocSize, 分配的数据块大小
  544. * @param iPageId, 该内存所属的TC_Span的起始页号
  545. * @param iIndex, 该内存所属的TC_Span的按iAllocSize大小划分后的第iIndex个
  546. * @return void *
  547. */
  548. void* allocate(size_t iNeedSize, size_t &iAllocSize, size_t &iPageId, size_t &iIndex);
  549. /**
  550. * 释放内存, 根据该内存所属TC_Span的起始页和该TC_Span按大小类别划分后的第iIndex个
  551. * @param iPageId, 该内存所属的TC_Span的起始页号
  552. * @param iIndex, 该内存所属的TC_Span的按iAllocSize大小划分后的第iIndex个
  553. */
  554. void deallocate(size_t iPageId, size_t iIndex);
  555. /**
  556. * 重建
  557. */
  558. void rebuild();
  559. /**
  560. * 修改更新到内存中
  561. */
  562. void doUpdate(bool bUpdate = false);
  563. /**
  564. * 头部内存块
  565. */
  566. #pragma pack(1)
  567. struct tagChunkAllocatorHead
  568. {
  569. bool _bProtectedArea;
  570. size_t _iSize;
  571. size_t _iTotalSize;
  572. size_t _iNext;
  573. };
  574. #pragma pack()
  575. /**
  576. * 头部内存块大小
  577. */
  578. static size_t getHeadSize() { return sizeof(tagChunkAllocatorHead); }
  579. /**
  580. * 传递给此内存分配器的内存块大小要不小于函数的返回值
  581. */
  582. static size_t getNeedMinSize() { return sizeof(tagChunkAllocatorHead) + TC_Page::getMinMemSize(); }
  583. protected:
  584. void init(void *pAddr);
  585. void _connect(void *pAddr);
  586. TC_MallocChunkAllocator *lastAlloc();
  587. //禁止copy构造
  588. TC_MallocChunkAllocator(const TC_MallocChunkAllocator &);
  589. //禁止复制
  590. TC_MallocChunkAllocator& operator=(const TC_MallocChunkAllocator &);
  591. private:
  592. /**
  593. * 头指针
  594. */
  595. tagChunkAllocatorHead *_pHead;
  596. /**
  597. * chunk开始的指针
  598. */
  599. void *_pChunk;
  600. /**
  601. * chunk分配类
  602. */
  603. TC_Page _page;
  604. /**
  605. * 后续的多块分配器
  606. */
  607. TC_MallocChunkAllocator *_nallocator;
  608. };
  609. }
  610. #endif