ReactOS-Freeldr内存管理

Freeldr内存管理模块的代码大多在freeldr/freeldr/mm文件夹中。我们从初始化部分开始看起。
freeldr/freeldr/mm/Meminit.c
  1. BOOLEAN MmInitializeMemoryManager(VOID)
  2. {
  3. // 获得内存总页数
  4. TotalPagesInLookupTable = MmGetAddressablePageCountIncludingHoles();
  5. // 为LookupTable生成空间
  6. PageLookupTableAddress = MmFindLocationForPageLookupTable(TotalPagesInLookupTable);
  7. LastFreePageHint = TotalPagesInLookupTable;
  8. if (PageLookupTableAddress == 0)
  9. {
  10. printf("Error initializing memory manager!/n");
  11. return FALSE;
  12. }
  13. // 初始化LookupTable
  14. MmInitPageLookupTable(PageLookupTableAddress, TotalPagesInLookupTable);
  15. MmUpdateLastFreePageHint(PageLookupTableAddress, TotalPagesInLookupTable);
  16. // 获得LookupTable中的空闲页数量
  17. FreePagesInLookupTable = MmCountFreePagesInLookupTable(PageLookupTableAddress, TotalPagesInLookupTable);
  18. // 初始化堆
  19. MmInitializeHeap(PageLookupTableAddress);
  20. return TRUE;
  21. }
首先使用MmGetAddressablePageCountIncludingHoles获得一共有多少内存页(4kb),这其中包括了BIOS AREA等不能被系统使用的内存。
MmInitializeMemoryManager -> MmGetAddressablePageCountIncludingHoles
freeldr/freeldr/mm/mm.c
  1. ULONG MmGetAddressablePageCountIncludingHoles(VOID)
  2. {
  3. MEMORY_DESCRIPTOR* MemoryDescriptor = NULL;
  4. ULONG EndPage = 0;
  5. /* 使用int 0x15等调用获得一个内存描述符, 这个描述符的格式和int 0x15返回的相同同 */
  6. while ((MemoryDescriptor = ArcGetMemoryDescriptor(MemoryDescriptor)) != NULL)
  7. {
  8. /* 找到最高的页号 */
  9. if (MemoryDescriptor->BasePage + MemoryDescriptor->PageCount > EndPage)
  10. {
  11. EndPage = MemoryDescriptor->BasePage + MemoryDescriptor->PageCount;
  12. }
  13. }
  14. return EndPage;
  15. }
他使用int 0x15中断获得每个内存描述符,找到最大的内存页并返回。
获得了内存页数后,调用 MmFindLocationForPageLookupTable从空闲内存页(Memroy_Free)的最高地址找到可以容纳LookupTable的空闲。
LookupTable是一个PAGE_LOOIUP_TABLE_ITEM数组,每个数组元素描述了一个页的类型(LoaderFree等), 当PageAllocated为已分配时(除LoaderFree外), PageAllocationLength为分配的块数,如果该页不是分配时的第一个页这个值为0。
所以内存有多少页LookupTable数组就有多少个元素。
  1. typedef struct
  2. {
  3. TYPE_OF_MEMORY PageAllocated; // Type of allocated memory (LoaderFree if this memory is free)
  4. ULONG PageAllocationLength; // Number of pages allocated (or zero if this isn't the first page in the chain)
  5. } PAGE_LOOKUP_TABLE_ITEM, *PPAGE_LOOKUP_TABLE_ITEM;
MmInitializeMemoryManager -> MmFindLocationForPageLookupTable
freeldr/freeldr/mm/meminit.c
  1. PVOID MmFindLocationForPageLookupTable(ULONG TotalPageCount)
  2. {
  3. MEMORY_DESCRIPTOR* MemoryDescriptor = NULL;
  4. ULONG PageLookupTableSize;
  5. ULONG PageLookupTablePages;
  6. ULONG PageLookupTableStartPage = 0;
  7. PVOID PageLookupTableMemAddress = NULL;
  8. /* 首先计算生成LoopupTable需要多少页 */
  9. PageLookupTableSize = TotalPageCount * sizeof(PAGE_LOOKUP_TABLE_ITEM);
  10. PageLookupTablePages = PageLookupTableSize / MM_PAGE_SIZE;
  11. /* 找到可以容纳LoopupTable的最高内存地址 */
  12. while ((MemoryDescriptor = ArcGetMemoryDescriptor(MemoryDescriptor)) != NULL)
  13. {
  14. if (MemoryDescriptor->MemoryType != MemoryFree)
  15. { /* 该内存块被占用 */
  16. continue;
  17. }
  18. if (MemoryDescriptor->PageCount < PageLookupTablePages)
  19. { /* 这个内存块不够大 */
  20. continue;
  21. }
  22. if (MemoryDescriptor->BasePage < PageLookupTableStartPage)
  23. { /* 该内存块的地址比以找到的地 */
  24. continue;
  25. }
  26. /* 这里获得的是容纳LoopupTable的最高内存地址 */
  27. PageLookupTableStartPage = MemoryDescriptor->BasePage;
  28. PageLookupTableMemAddress = (PVOID)((ULONG_PTR)
  29. (MemoryDescriptor->BasePage + MemoryDescriptor->PageCount) * MM_PAGE_SIZE
  30. - PageLookupTableSize);
  31. }
  32. return PageLookupTableMemAddress;
  33. }
这个函数搜索计算机中的所有内存块,找到可以容纳PageLookupTable的最高地址的内存。返回给调用者。
现在找到了放置PageLookupTalbe的空间,调用 MmInitPageLookupTable对他进行初始化。这个函数有两个参数,PageLookupTable的地址和它的元素个数(也就是我们页的个数)
MmInitializeMemoryManager -> MmInitPageLookupTable
freeldr/freeldr/mm/meminit.c
  1. VOID MmInitPageLookupTable(PVOID PageLookupTable, ULONG TotalPageCount)
  2. {
  3. MEMORY_DESCRIPTOR* MemoryDescriptor = NULL;
  4. TYPE_OF_MEMORY MemoryMapPageAllocated;
  5. ULONG PageLookupTableStartPage;
  6. ULONG PageLookupTablePageCount;
  7. // 首先把所有的页标注为LoaderFirmwarePermanent
  8. MmMarkPagesInLookupTable(PageLookupTable, 0, TotalPageCount, LoaderFirmwarePermanent);
  9. while ((MemoryDescriptor = ArcGetMemoryDescriptor(MemoryDescriptor)) != NULL)
  10. {
  11. // 把int 0x15返回的内存类型转换为Loader中对应的
  12. switch (MemoryDescriptor->MemoryType)
  13. {
  14. case MemoryFree:
  15. { // 空闲内存
  16. MemoryMapPageAllocated = LoaderFree;
  17. break;
  18. }
  19. case MemoryFirmwarePermanent:
  20. { // BIOS中断表等硬件使用的内存
  21. MemoryMapPageAllocated = LoaderFirmwarePermanent;
  22. break;
  23. }
  24. case MemoryFirmwareTemporary:
  25. { // 堆栈、缓冲区等
  26. MemoryMapPageAllocated = LoaderFirmwareTemporary;
  27. break;
  28. }
  29. case MemoryLoadedProgram:
  30. { // freeldr代码本身
  31. MemoryMapPageAllocated = LoaderLoadedProgram;
  32. break;
  33. }
  34. case MemorySpecialMemory:
  35. { // 其它类型, 包括Prot下的堆栈等
  36. MemoryMapPageAllocated = LoaderSpecialMemory;
  37. break;
  38. }
  39. default:
  40. {
  41. MemoryMapPageAllocated = LoaderSpecialMemory;
  42. break;
  43. }
  44. }
  45. // 在PageLoopupTable中标记该段内存
  46. MmMarkPagesInLookupTable(PageLookupTable, MemoryDescriptor->BasePage, MemoryDescriptor->PageCount, MemoryMapPageAllocated);
  47. }
  48. // 将PageLookupTable本身标记为LoaderFirmwareTemporary
  49. PageLookupTableStartPage = MmGetPageNumberFromAddress(PageLookupTable);
  50. PageLookupTablePageCount = MmGetPageNumberFromAddress((PVOID)((ULONG_PTR)PageLookupTable + ROUND_UP(TotalPageCount * sizeof(PAGE_LOOKUP_TABLE_ITEM), MM_PAGE_SIZE))) - PageLookupTableStartPage;
  51. MmMarkPagesInLookupTable(PageLookupTable, PageLookupTableStartPage, PageLookupTablePageCount, LoaderFirmwareTemporary);
  52. }
这个函数把ArcGetMemoryDescriptor返回的内存类型转换为Loader中的对应类型,并调用MmMarkPagesInLookupTable标记对应内存。最后PageLookupTable本身占用的空间被标记为LoaderFirmwareTemporary。
这个函数返回后PageLookupTable初始化完毕, 每个页在其中都有对应的表项。
 
最后MmInitializeMemoryManager使用MmInitializeHeap初始化堆。
  1. VOID MmInitializeHeap(PVOID PageLookupTable)
  2. {
  3. ULONG PagesNeeded;
  4. ULONG HeapStart;
  5. MEMORY_TYPE Type;
  6. PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable = (PPAGE_LOOKUP_TABLE_ITEM)PageLookupTable;
  7. // 首先保留1mb -- 2mb这段空间, 这段空间之后用作内核, 所以不能使用
  8. Type = RealPageLookupTable[0x100].PageAllocated;
  9. MmMarkPagesInLookupTable(PageLookupTableAddress, 0x100, 0xFF, LoaderSystemCode);
  10. // 从PageLookupTable中寻找足够大的空间
  11. PagesNeeded = HEAP_PAGES + STACK_PAGES;
  12. HeapStart = MmFindAvailablePages(PageLookupTable, TotalPagesInLookupTable, PagesNeeded, FALSE);
  13. MmMarkPagesInLookupTable(PageLookupTableAddress, 0x100, 0xFF, Type);
  14. if (HeapStart == 0)
  15. return;
  16. // 初始化堆, 这是开源的bget堆管理库
  17. bpool(HeapStart << MM_PAGE_SHIFT, PagesNeeded << MM_PAGE_SHIFT);
  18. // 把对占用的页标记为LoaderOsloaderHeap
  19. MmMarkPagesInLookupTable(PageLookupTableAddress, HeapStart, PagesNeeded, LoaderOsloaderHeap);
  20. }
这个函数很简单,找到合适的内存后调用bpool初始化堆,把堆占用的内存标记为LoaderOsLoaderHeap。
到这里内存管理部分初始化就完毕了。
 
最后来看看怎么分配内存。
freeldr分配内存主要使用两个函数MmHeapAlloc从堆中分配、MmAllocateMemoryWithType直接从LookupTable中分配。
MmHeapAlloc主要用在分配小于1个页的内存时使用, 它就是不bget函数简单的包装。
  1. PVOID MmHeapAlloc(ULONG MemorySize)
  2. {
  3. PVOID Result;
  4. // 调用bget从堆中分配内存
  5. Result = bget(MemorySize);
  6. return Result;
  7. }
当分配大于一个页的内存时, 一般使用MmAllocateMemoryWithType函数, 这个函数直接动LookupTable中搜索适当的内存块。
  1. PVOID MmAllocateMemoryWithType(ULONG MemorySize, TYPE_OF_MEMORY MemoryType)
  2. {
  3. ULONG PagesNeeded;
  4. ULONG FirstFreePageFromEnd;
  5. PVOID MemPointer;
  6. if (MemorySize == 0)
  7. {
  8. return NULL;
  9. }
  10. // 大小按照页对齐
  11. MemorySize = ROUND_UP(MemorySize, 4);
  12. PagesNeeded = ROUND_UP(MemorySize, MM_PAGE_SIZE) / MM_PAGE_SIZE;
  13. // 空间不够
  14. if (FreePagesInLookupTable < PagesNeeded)
  15. {
  16. return NULL;
  17. }
  18. // 从LookupTable中找到适当的空间
  19. FirstFreePageFromEnd = MmFindAvailablePages(PageLookupTableAddress, TotalPagesInLookupTable, PagesNeeded, FALSE);
  20. if (FirstFreePageFromEnd == 0)
  21. {
  22. return NULL;
  23. }
  24. // 分配这段空间并把这段空间标记为MemoryType
  25. MmAllocatePagesInLookupTable(PageLookupTableAddress, FirstFreePageFromEnd, PagesNeeded, MemoryType);
  26. FreePagesInLookupTable -= PagesNeeded;
  27. MemPointer = (PVOID)((ULONG_PTR)FirstFreePageFromEnd * MM_PAGE_SIZE);
  28. // LoaderPagesSpanned记录了freeldr和其使用的内存占用的空间
  29. if ((((ULONG_PTR)MemPointer + MemorySize + PAGE_SIZE - 1) >> PAGE_SHIFT) > LoaderPagesSpanned)
  30. LoaderPagesSpanned = (((ULONG_PTR)MemPointer + MemorySize + PAGE_SIZE - 1) >> PAGE_SHIFT);
  31. return MemPointer;
  32. }
这一这个函数最后更新了LoaderPagesSpanned全局变量, 这个变量记录了freeldr占用的空间大小,最后freeldr打开分页时需要用到这个值。

你可能感兴趣的:(ReactOS代码精读)