BareMetalOS 如何在没有 malloc、brk 或 mmap 的情况下在 Assembly 中分配内存?

Lan*_*ard 2 c assembly memory-management

作为学习实验,我有兴趣在汇编中创建一个哈希表(OSX 上 NASM 中的 x86-64)。要求之一是能够动态分配/管理内存。

在查看了许多关于如何在汇编中分配内存的资源后,他们中的大多数人推荐brkmmap系统调用。我还没有确切地了解这些是如何工作的,因为我在BareMetal-OS中发现了另一种不使用任何系统调用的内存分配实现(复制了下面的代码)。

我的问题是,他们是如何做到这一点的?您能否为没有系统编程背景且不熟悉汇编的人解释其汇编中执行内存分配的相关指令?想了解如何在汇编中实现内存分配的原因是为了能够在汇编中实现哈希表。

刚接触汇编(我主要是做 JavaScript),还没有找到任何关于汇编内存分配的详细资源,我不知道从哪里开始。这对你来说可能很明显,但你有背景,而我没有。我在过去一两周完成了一些汇编,所以我了解有关mov寄存器和跳转命令的基础知识,但还不了解他们为实现这些内存内容所做的其他工作。我的想法是,如果他们可以在没有brkor 的情况下在汇编中实现内存分配mmap,那么我想这样做,因为那样我真的是在没有任何系统层的情况下直接操作内存,而且看起来你真的可以微调一些东西。

这是他们从 GitHub 复制的代码:

https://github.com/ReturnInfinity/BareMetal-OS/blob/master/os/syscalls/memory.asm

# =============================================================================
# BareMetal -- a 64-bit OS written in Assembly for x86-64 systems
# Copyright (C) 2008-2014 Return Infinity -- see LICENSE.TXT
#
# Memory functions
# =============================================================================

align 16
db 'DEBUG: MEMORY   '
align 16


# -----------------------------------------------------------------------------
# os_mem_allocate -- Allocates the requested number of 2 MiB pages
#  IN:  RCX = Number of pages to allocate
# OUT:  RAX = Starting address (Set to 0 on failure)
# This function will only allocate continuous pages
os_mem_allocate:
  push rsi
  push rdx
  push rbx

  cmp rcx, 0
  je os_mem_allocate_fail   # At least 1 page must be allocated

  # Here, we'll load the last existing page of memory in RSI.
  # RAX and RSI instructions are purposefully interleaved.

  xor rax, rax
  mov rsi, os_MemoryMap   # First available memory block
  mov eax, [os_MemAmount]   # Total memory in MiB from a double-word
  mov rdx, rsi      # Keep os_MemoryMap unmodified for later in RDX         
  shr eax, 1      # Divide actual memory by 2

  sub rsi, 1
  std       # Set direction flag to backward
  add rsi, rax      # RSI now points to the last page

os_mem_allocate_start:      # Find a free page of memory, from the end.
  mov rbx, rcx      # RBX is our temporary counter

os_mem_allocate_nextpage:
  lodsb
  cmp rsi, rdx      # We have hit the start of the memory map, no more free pages
  je os_mem_allocate_fail

  cmp al, 1
  jne os_mem_allocate_start # Page is taken, start counting from scratch

  dec rbx       # We found a page! Any page left to find?
  jnz os_mem_allocate_nextpage

os_mem_allocate_mark:     # We have a suitable free series of pages. Allocate them.
  cld       # Set direction flag to forward

  xor rdi, rsi      # We swap rdi and rsi to keep rdi contents.
  xor rsi, rdi
  xor rdi, rsi

  # Instructions are purposefully swapped at some places here to avoid 
  # direct dependencies line after line.
  push rcx      # Keep RCX as is for the 'rep stosb' to come
  add rdi, 1
  mov al, 2
  mov rbx, rdi      # RBX points to the starting page
  rep stosb
  mov rdi, rsi      # Restoring RDI
  sub rbx, rdx      # RBX now contains the memory page number
  pop rcx       # Restore RCX

  # Only dependency left is between the two next lines.
  shl rbx, 21     # Quick multiply by 2097152 (2 MiB) to get the starting memory address
  mov rax, rbx      # Return the starting address in RAX
  jmp os_mem_allocate_end

os_mem_allocate_fail:
  cld       # Set direction flag to forward
  xor rax, rax      # Failure so set RAX to 0 (No pages allocated)

os_mem_allocate_end:
  pop rbx
  pop rdx
  pop rsi
  ret
# -----------------------------------------------------------------------------


# -----------------------------------------------------------------------------
# os_mem_release -- Frees the requested number of 2 MiB pages
#  IN:  RAX = Starting address
# RCX = Number of pages to free
# OUT:  RCX = Number of pages freed
os_mem_release:
  push rdi
  push rcx
  push rax

  shr rax, 21     # Quick divide by 2097152 (2 MiB) to get the starting page number
  add rax, os_MemoryMap
  mov rdi, rax
  mov al, 1
  rep stosb

  pop rax
  pop rcx
  pop rdi
  ret
# -----------------------------------------------------------------------------


# -----------------------------------------------------------------------------
# os_mem_get_free -- Returns the number of 2 MiB pages that are available
#  IN:  Nothing
# OUT:  RCX = Number of free 2 MiB pages
os_mem_get_free:
  push rsi
  push rbx
  push rax

  mov rsi, os_MemoryMap
  xor rcx, rcx
  xor rbx, rbx

os_mem_get_free_next:
  lodsb
  inc rcx
  cmp rcx, 65536
  je os_mem_get_free_end
  cmp al, 1
  jne os_mem_get_free_next
  inc rbx
  jmp os_mem_get_free_next

os_mem_get_free_end:
  mov rcx, rbx

  pop rax
  pop rbx
  pop rsi
  ret
# -----------------------------------------------------------------------------


# -----------------------------------------------------------------------------
# os_mem_copy -- Copy a number of bytes
#  IN:  RSI = Source address
# RDI = Destination address
# RCX = Number of bytes to copy
# OUT:  Nothing, all registers preserved
os_mem_copy:
  push rdi
  push rsi
  push rcx

  rep movsb     # Optimize this!

  pop rcx
  pop rsi
  pop rdi
  ret
# -----------------------------------------------------------------------------


# =============================================================================
# EOF
Run Code Online (Sandbox Code Playgroud)

另请注意,我已经阅读了许多关于在 C 中创建哈希表的资源,我在此处复制其中之一(其中包含 C 代码和相应的程序集)。但是,几乎所有 C 示例都使用malloc,我想避免这种情况。我试图在完全不依赖 C 的情况下学习汇编。

此外,来自 Quora 的这个资源有助于指出malloc.c源代码中使用brk和的地方mmap。但是,我还没有研究它,因为发现了 BareMetal-OSmemory.asm代码,它似乎甚至不使用那些系统调用就分配内存。那么问题来了,他们是怎么做到的?你能解释一下他们汇编中执行内存分配的相关指令吗?

更新

这本书有助于解释关于以下内存的内部几乎一切mmapbrk,这一切都在执行操作系统的区域。http://www.amazon.com/Modern-Operating-Systems-4th-Edition/dp/013359162X

use*_*109 5

为了管理内存,您的代码需要“拥有”一些内存。问题在于,在任何具有操作系统的机器上,操作系统拥有所有内存。所以你的代码必须向操作系统请求一些内存,它可以用brk, or mmap, or来做malloc

因此,例如,如果您想在汇编中编写内存管理器,并且您有一台具有 4GB 内存的机器,那么malloc在程序开始时请求 1GB 内存,然后以任何方式管理该内存并不是不合理的你喜欢。

BareMetal-OS 中的汇编代码确实不适用于您的情况,因为 BareMetal操作系统,因此不需要向任何人询问内存。它已经拥有所有内存,并且可以随心所欲地管理它。

  • @LancePollard 这不是废话,但它不适用于编写在操作系统之上的用户空间中运行的内存分配器,因为这是一个非常非常不同的环境。关于如何编写简单的用户空间内存分配器,请参见例如 [this](http://www.ibm.com/developerworks/library/l-memory/)。(这是 C 代码,如果需要,您可以将其转换为汇编。)无论您使用汇编还是 C,您都需要向操作系统询问内存(例如,通过调用 sbrk() 系统调用),并且一旦您得到了那块内存,你可以从中分配你自己的块。 (2认同)