Files
ViperOS/VKernel/Kernel/mm/mm.vp
T
TermiNexus 7e7204a09a fix(mm,explorer): slab 页严格归属与分配位图;关闭按钮按下态与松手关闭
① [mm] BAD blk 的解码:site=2=free_block、reason=1=magic 失配;blk 实际是 slab_page+16(页在 0x1b56020),sz 是该页 free_head,而非块大小。被踩的是页头前 8~16 字节,最可能是无锁 buddy 在 usb.init(sched.enable 之后)与用户进程 MMAP 并发 malloc 时重复授予同一 8KB 块。② mm 加固:_slab_find_page 不再只看 magic,新增 _slab_page_owns(block_ptr 回指、指针在对象区内、按 obj_size 对齐、序号 < total_objs);slab_page 新增 alloc_bits,只有已分配对象可 free,重复释放/野指针被拒,避免 free_count 提前到 total_objs 把仍在用的页拆回 buddy;free_obj 拒绝已全空继续收对象;_create_page 失败路径清零 magic。③ hid_ascii 仅只读且已有边界检查,非本次堆损坏来源;usb_kbd 把 code >= 64 改为显式早返回。④ 关闭按钮:按下只置 close_down 并置脏(按钮画按下态:CLASSIC 40,40,70→20,20,45;WIN7/XP 红底 200,50,50→140,25,25;WIN10/11 补深灰底),松手且仍命中才发 CLOSE + used=0,移出则撤销按下态恢复;几何复用 _close_btn_* 同一套函数。
2026-10-17 16:23:44 +08:00

884 lines
37 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import bootinfo
import paging.paging as paging
import string
import drivers.serial.uart.serial as serial
import viperlib
# 伙伴系统常量定义
define MIN_BLOCK_SIZE = 4096 # 最小块大小(4KB)
define MAX_BLOCK_SIZE = 268435456 # 最大块大小(256MB)
define MAX_ORDER = 18 # 最大阶数(2^18 = 262144)
# 内存块状态
define BLOCK_FREE = 0 # 空闲
define BLOCK_USED = 1 # 已使用
# 内存类型定义
define MEMORY_TYPE_AVAILABLE: uint64 = 7
define MEMORY_TYPE_RESERVED: uint64 = 0
define MEMORY_TYPE_LOADER_CODE: uint64 = 1
define MEMORY_TYPE_LOADER_DATA: uint64 = 2
define MEMORY_TYPE_BOOT_SERVICES_CODE: uint64 = 3
define MEMORY_TYPE_BOOT_SERVICES_DATA: uint64 = 4
define MEMORY_TYPE_RUNTIME_SERVICES_CODE: uint64 = 5
define MEMORY_TYPE_RUNTIME_SERVICES_DATA: uint64 = 6
define MEMORY_TYPE_ACPI_RECLAIMABLE: uint64 = 9
define MEMORY_TYPE_ACPI_NVS: uint64 = 10
define MEMORY_TYPE_BAD_MEMORY: uint64 = 8
# 内存区域结构
class memory_region:
start: uint64
end: uint64
size: uint64
type: uint32
next: memory_region*
# 全局内存区域链表
static memory_regions: memory_region* = None
# 坏块报告限流计数:同一处坏块会被反复检出(每次分配都撞到坏表头),前几次打全量、
# 之后只提示一次,避免串口被同一份报告淹没。
static _mm_bad_reports: int = 0
# 内存块结构
# 说明:state/order 之后正好有 2 字节 padding,magic 塞进去 ⇒ 结构仍是 32 字节、
# 布局不变(next@+0x10、prev@+0x18),不会改变任何载荷偏移。
class memory_block:
size: uint64 # 块大小
state: uint8 # 块状态
order: uint8 # 块阶数
magic: uint32 # 块头魔数:任何字段被越界写覆盖时通常先在这里失配(比范围/对齐检查更早)
next: memory_block* # 下一个块
prev: memory_block* # 上一个块
# 块头魔数("BLK!")
define BLOCK_MAGIC = 0x424C4B21
# 伙伴系统结构
class __buddy_system:
free_lists: [MAX_ORDER + 1]memory_block*
total_memory: uint64 # 总内存大小
used_memory: uint64 # 已使用内存大小
free_memory: uint64 # 空闲内存大小
start_addr: rawptr # 内存起始地址
end_addr: rawptr # 内存结束地址
define SLAB_MAGIC = 0x534C4142
define SLAB_NUM_SIZES = 6
define SLAB_THRESHOLD = 1024
define SLAB_BITMAP_BYTES = 32
class slab_page:
magic: uint32
obj_size: uint16
free_count: uint16
data_offset: uint16
total_objs: uint16
free_head: rawptr
block_ptr: memory_block*
next: slab_page*
prev: slab_page*
# 每对象 1 bit:只有置位的对象才能被 free。释放未分配对象(重复释放/野指针撞上
# 页内空闲对象)会让 free_count 提前到达 total_objs ⇒ 页被拆回 buddy,而页内仍
# 有活对象,随后它的 free 就只剩 magic=0 的旧页头(现场 0x1b56030 即 page+16)。
alloc_bits: [SLAB_BITMAP_BYTES]uint8
# 4K对齐函数
def align_4k(addr: uint64) -> uint64:
return (addr + 0xFFF) & ~0xFFF
# 计算阶数
static def get_order(size: uint64) -> int:
order: int = 0
while size > MIN_BLOCK_SIZE:
size >>= 1
order += 1
return order
# 计算块大小
static def get_block_size(order: int) -> uint64:
# 4096 << 20 = 4GB 超出 i32 范围 → 0x0 → 伙伴初始化无限循环(bs=0x0)。
# 必须用 64 位移位(4096<<20 = 4294967296)。
return uint64(MIN_BLOCK_SIZE) << uint64(order)
class _BuddySystemObject:
# 全局伙伴系统实例
buddy: __buddy_system
# 初始化伙伴系统
def __init__(self, start_addr: rawptr, size: uint64):
for i in range(MAX_ORDER + 1):
self.buddy.free_lists[i] = None
self.buddy.total_memory = size
self.buddy.used_memory = 0
self.buddy.free_memory = size
self.buddy.start_addr = start_addr
self.buddy.end_addr = rawptr(uint64(start_addr) + size)
remaining: uint64 = size
cur_addr: uint64 = uint64(start_addr)
while remaining >= MIN_BLOCK_SIZE:
order: int = 0
tmp: uint64 = remaining
while tmp > MIN_BLOCK_SIZE:
tmp >>= 1
order += 1
block_size: uint64 = get_block_size(order)
while block_size > remaining and order > 0:
order -= 1
block_size = get_block_size(order)
if block_size > remaining: break
block: memory_block* = memory_block*(cur_addr)
block.size = block_size
block.state = BLOCK_FREE
block.order = uint8(order)
block.magic = uint32(BLOCK_MAGIC)
block.next = None
block.prev = None
if order <= MAX_ORDER:
if self.buddy.free_lists[order]:
block.next = self.buddy.free_lists[order]
self.buddy.free_lists[order].prev = block
self.buddy.free_lists[order] = block
cur_addr += block_size
remaining -= block_size
# 分割内存块
def split_block(self, block: memory_block*, target_order: int) -> memory_block*:
current_order: int = block.order
# 从当前阶数向下分割,直到达到目标阶数
while current_order > target_order:
current_order -= 1
new_block_size: uint64 = get_block_size(current_order)
# 创建新块
new_block: memory_block* = memory_block*(uint64(block) + new_block_size)
new_block.size = new_block_size
new_block.state = BLOCK_FREE
new_block.order = uint8(current_order)
new_block.magic = uint32(BLOCK_MAGIC)
new_block.next = None
new_block.prev = None
# 更新原块大小和阶数
block.size = new_block_size
block.order = uint8(current_order)
# 将新块添加到对应阶数的空闲链表
if current_order <= MAX_ORDER:
new_block.next = self.buddy.free_lists[current_order]
if self.buddy.free_lists[current_order]:
self.buddy.free_lists[current_order].prev = new_block
self.buddy.free_lists[current_order] = new_block
return block
# 块头一致性检查:0 = OK;1 = magic 失配;2 = size/order 不一致;3 = state 不符;4 = prev/next 越界或未对齐
# want_state < 0 表示不校验 state。任一项不符 ⇒ 块头被越界写 / 释放后又被写覆盖。
static def _check_block(self, block: memory_block*, want_state: int) -> int:
if block.magic != uint32(BLOCK_MAGIC): return 1
if int(block.order) > MAX_ORDER: return 2
if block.size != get_block_size(int(block.order)): return 2
if want_state >= 0 and int(block.state) != want_state: return 3
_s: uint64 = uint64(self.buddy.start_addr)
_e: uint64 = uint64(self.buddy.end_addr)
_pv: uint64 = uint64(block.prev)
_nx: uint64 = uint64(block.next)
if _pv != 0 and (_pv < _s or _pv >= _e or (_pv & 7) != 0): return 4
if _nx != 0 and (_nx < _s or _nx >= _e or (_nx & 7) != 0): return 4
return 0
# 坏块报告(加固):块头一行摘要。site: 1=allocate_block 2=free_block 3=merge_blocks
# ctx = 触发检查的那一阶(merge/allocate 的 order)。
static def _report_bad_block(self, site: int, reason: int, block: memory_block*, order: int):
global _mm_bad_reports
_mm_bad_reports += 1
if _mm_bad_reports > 4:
if _mm_bad_reports == 5:
serial.puts("[mm] (further BAD blk reports suppressed)\n")
return
_rb: [224]char
viperlib.snprintf(&_rb, 224, "[mm] BAD blk site=%d reason=%d blk=0x%lx sz=0x%lx st=%d ord=%d ctx=%d magic=0x%lx nxt=0x%lx prv=0x%lx\n",
site, reason, uint64(block), block.size, int(block.state), int(block.order), order,
uint64(uint32(block.magic)), uint64(block.next), uint64(block.prev))
serial.puts(&_rb)
# 合并内存块
static def merge_blocks(self, block: memory_block*) -> memory_block*:
order: int = block.order
while order < MAX_ORDER:
block_size: uint64 = get_block_size(order)
block_addr: uint64 = uint64(block) - uint64(self.buddy.start_addr)
buddy_addr: uint64 = block_addr ^ block_size
# 计算伙伴块地址
buddy: memory_block* = memory_block*(uint64(self.buddy.start_addr) + buddy_addr)
# 检查伙伴块是否存在且空闲
if buddy >= memory_block*(self.buddy.end_addr):
break # 伙伴块超出内存范围
if int(buddy.state) != BLOCK_FREE or int(buddy.order) != order:
break # 伙伴块不空闲或阶数不匹配
# 加固:摘链前先校验伙伴块头(magic / size-order 一致 / prev-next 在堆内且对齐)。
# 损坏时只报不改 —— 跳过本次合并、空闲链保持原样,不再让坏值流到
# `buddy.prev.next = buddy.next`(现场那条写非规范地址 → #GP err=0,rip=0x119e50)。
_rc: int = self._check_block(buddy, BLOCK_FREE)
if _rc != 0:
self._report_bad_block(3, _rc, buddy, order)
break
# 从空闲链表中移除伙伴块
if buddy.prev: buddy.prev.next = buddy.next
else: self.buddy.free_lists[order] = buddy.next
if buddy.next:
buddy.next.prev = buddy.prev
# 合并块
merged_block: memory_block* = block if block < buddy else buddy
merged_block.size = get_block_size(order + 1)
merged_block.order = uint8(order + 1)
block = merged_block
order += 1
return block
# 分配内存块
static def allocate_block(self, size: uint64) -> memory_block*:
# 计算所需阶数
order: int = get_order(size)
if order > MAX_ORDER: return None # 所需块大小超过最大限制
# 查找合适的空闲块
found_order: int = -1
for i in range(order, MAX_ORDER + 1):
if self.buddy.free_lists[i]:
found_order = i
break
if found_order == -1:
# [DIAG] 池面耗尽/链表坏:请求大小、所需阶数与整体水位(一眼分辨"用光"还是"碎片化")
_adb: [176]char
viperlib.snprintf(&_adb, 176, "[mm] allocate_block(size=%lu) need order=%d -> NO FREE BLOCK (used=0x%lx free=0x%lx total=0x%lx)\n",
size, order, self.buddy.used_memory, self.buddy.free_memory, self.buddy.total_memory)
serial.puts(&_adb)
return None
# 取出空闲块
block: memory_block* = self.buddy.free_lists[found_order]
# 加固:摘链前校验块头(magic + size/order + 双链范围)。坏则放弃本次分配、
# 保持空闲链原样 —— 否则紧接着的 `block.next.prev = None` 就会踩坏值。
_arc: int = self._check_block(block, BLOCK_FREE)
if _arc != 0:
self._report_bad_block(1, _arc, block, found_order)
return None
# 从空闲链表中移除块
if block.next: block.next.prev = None
self.buddy.free_lists[found_order] = block.next
# 如果块大小大于所需大小,分割块
if found_order > order:
block = self.split_block(block, order)
# 标记块为已使用
block.state = BLOCK_USED
# 更新内存统计
self.buddy.used_memory += block.size
self.buddy.free_memory -= block.size
return block
# 释放内存块
def free_block(self, block: memory_block*):
# 加固:先校验传入块头(magic + size/order 一致 + state 必须是 USED + 双链在堆内且对齐)。
# 坏块**拒绝入链并报出**:重复释放 / 野指针 / 头被越界写,都会在这里被早抓到,
# 而不是等到某次 merge 写非规范地址才 #GP(err=0)(现场 rip=0x119e50)。
_frc: int = self._check_block(block, BLOCK_USED)
if _frc != 0:
self._report_bad_block(2, _frc, block, int(block.order))
return
# 标记块为空闲
block.state = BLOCK_FREE
# 更新内存统计
self.buddy.used_memory -= block.size
self.buddy.free_memory += block.size
# 尝试合并块
block = self.merge_blocks(block)
# 将块添加到对应阶数的空闲链表
order: int = block.order
if order <= MAX_ORDER:
block.next = self.buddy.free_lists[order]
block.prev = None
if self.buddy.free_lists[order]:
self.buddy.free_lists[order].prev = block
self.buddy.free_lists[order] = block
BuddySystemObject: _BuddySystemObject
def _slab_obj_size(size_class: int) -> uint16:
if size_class == 0: return 32
if size_class == 1: return 64
if size_class == 2: return 128
if size_class == 3: return 256
if size_class == 4: return 512
return 1024
def _slab_size_class(size: uint64) -> int:
if size <= 32: return 0
if size <= 64: return 1
if size <= 128: return 2
if size <= 256: return 3
if size <= 512: return 4
if size <= 1024: return 5
return -1
def _slab_read_ptr(addr: rawptr) -> rawptr:
result: rawptr
__asm__(f"""mov rax, [{asminp(addr, ASM_DESCR.REG_ANY)}]
mov {asmout(result, ASM_DESCR.OUTPUT_REG)}, rax""",
op=[ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RAX])
return result
def _slab_write_ptr(addr: rawptr, val: rawptr):
__asm__(f"""mov rax, {asminp(val, ASM_DESCR.REG_ANY)}
mov [{asminp(addr, ASM_DESCR.REG_ANY)}], rax""",
op=[ASM_DESCR.CLOBBER_RAX, ASM_DESCR.CLOBBER_MEMORY])
# 对象在页内的序号(-1 = 不在对象边界上)
def _slab_bit_index(page: slab_page*, ptr: rawptr) -> int:
if page.obj_size == 0: return -1
hdr_off: uint64 = memory_block.__sizeof__()
block: uint64 = uint64(page) - hdr_off
data: uint64 = block + uint64(page.data_offset)
if uint64(ptr) < data: return -1
return int((uint64(ptr) - data) / uint64(page.obj_size))
def _slab_bit_set(page: slab_page*, idx: int):
if idx < 0 or idx >= SLAB_BITMAP_BYTES * 8: return
byte_idx: int = idx / 8
page.alloc_bits[byte_idx] = uint8(page.alloc_bits[byte_idx] | uint8(1 << (idx & 7)))
def _slab_bit_clear(page: slab_page*, idx: int):
if idx < 0 or idx >= SLAB_BITMAP_BYTES * 8: return
byte_idx: int = idx / 8
page.alloc_bits[byte_idx] = uint8(page.alloc_bits[byte_idx] & ~uint8(1 << (idx & 7)))
def _slab_bit_test(page: slab_page*, idx: int) -> int:
if idx < 0 or idx >= SLAB_BITMAP_BYTES * 8: return 0
byte_idx: int = idx / 8
mask: uint8 = uint8(1 << (idx & 7))
if (page.alloc_bits[byte_idx] & mask) != 0: return 1
return 0
# slab 页归属校验:页头必须自洽(block_ptr 指回本页所在块),且 ptr 必须落在页内
# [data_offset, data_offset + total*obj) 范围并按其对象大小对齐。只查 magic 会把
# 大块分配头部残留的“看起来像页头”的数据误判为 slab 页,free 会写进页外并把仍在
# 使用的块还给 buddy(现场:页头 magic 被清零后 free(err) 落到 buddy 路径)。
def _slab_page_owns(page: slab_page*, ptr: rawptr, hdr_off: uint64) -> int:
if page.magic != SLAB_MAGIC: return 0
obj: uint64 = uint64(page.obj_size)
if obj == 0: return 0
block: uint64 = uint64(page) - hdr_off
if uint64(page.block_ptr) != block: return 0
if uint64(page.data_offset) < hdr_off: return 0
data: uint64 = block + uint64(page.data_offset)
if uint64(ptr) < data: return 0
off: uint64 = uint64(ptr) - data
if (off % obj) != 0: return 0
if (off / obj) >= uint64(page.total_objs): return 0
return 1
def _slab_find_page(ptr: rawptr) -> slab_page*:
if ptr is None: return None
hdr_off: uint64 = memory_block.__sizeof__()
page_addr: uint64 = uint64(ptr) & ~uint64(0xFFF)
page: slab_page* = slab_page*(page_addr + hdr_off)
if _slab_page_owns(page, ptr, hdr_off) != 0:
os: uint16 = page.obj_size
if os == 32 or os == 64 or os == 128 or os == 256 or os == 512 or os == 1024:
return page
page_addr2: uint64 = page_addr - 4096
page2: slab_page* = slab_page*(page_addr2 + hdr_off)
if _slab_page_owns(page2, ptr, hdr_off) != 0:
os2: uint16 = page2.obj_size
if os2 == 32 or os2 == 64 or os2 == 128 or os2 == 256 or os2 == 512 or os2 == 1024:
return page2
return None
class _SlabAllocator:
caches: [SLAB_NUM_SIZES]slab_page*
def __init__(self):
for i in range(SLAB_NUM_SIZES):
self.caches[i] = None
def _create_page(self, size_class: int) -> slab_page*:
obj_size: uint16 = _slab_obj_size(size_class)
aligned_size: uint16 = uint16((obj_size + 15) & ~15)
block: memory_block* = BuddySystemObject.allocate_block(4096 + memory_block.__sizeof__())
if not block:
# [DIAG] slab 建页拿不到伙伴块
_cpb: [96]char
viperlib.snprintf(&_cpb, 96, "[mm] slab _create_page(sc=%d) allocate_block(4128) -> NULL\n", size_class)
serial.puts(&_cpb)
return None
hdr_off: uint64 = memory_block.__sizeof__()
page: slab_page* = slab_page*(uint64(block) + hdr_off)
page.magic = SLAB_MAGIC
page.obj_size = aligned_size
d_off: uint16 = uint16((hdr_off + slab_page.__sizeof__() + 15) & ~15)
page.data_offset = d_off
total: uint16 = uint16((block.size - d_off) / aligned_size)
page.total_objs = total
page.free_count = total
page.block_ptr = block
page.next = None
page.prev = None
for bi in range(SLAB_BITMAP_BYTES):
page.alloc_bits[bi] = 0
data_start: uint64 = uint64(block) + d_off
# 加固:下面那个建链循环是分配器里唯一"成片写指针"的地方,一旦 total 因下溢/截断而
# 离谱,就会把 nxt 指针写到自己页外、覆盖相邻块头。先把 total 与地址范围校验成自洽。
if block.size < uint64(d_off) + uint64(aligned_size):
serial.puts("[mm] slab _create_page: block too small for page header - refused\n")
page.magic = 0 # 归还前抹掉页头,避免后续把它误认成有效 slab 页
BuddySystemObject.free_block(block)
return None
if uint64(total) * uint64(aligned_size) + uint64(d_off) > block.size:
serial.puts("[mm] slab _create_page: total*obj_size+d_off > block.size (would write outside page) - refused\n")
page.magic = 0 # 同上
BuddySystemObject.free_block(block)
return None
page.free_head = rawptr(data_start)
for i in range(1, total):
cur: rawptr = rawptr(data_start + uint64(i - 1) * aligned_size)
nxt: rawptr = rawptr(data_start + uint64(i) * aligned_size)
_slab_write_ptr(cur, nxt)
last: rawptr = rawptr(data_start + uint64(total - 1) * aligned_size)
_slab_write_ptr(last, None)
return page
def alloc(self, size_class: int) -> rawptr:
page: slab_page* = self.caches[size_class]
while page:
if page.free_count > 0: break
page = page.next
if not page:
page = self._create_page(size_class)
if not page: return None
page.next = self.caches[size_class]
if self.caches[size_class]:
self.caches[size_class].prev = page
self.caches[size_class] = page
obj: rawptr = page.free_head
_slab_bit_set(page, _slab_bit_index(page, obj))
page.free_head = _slab_read_ptr(obj)
page.free_count -= 1
return obj
def free_obj(self, ptr: rawptr, page: slab_page*):
# 只有“已分配”的对象能被 free:否则 free_count 会提前到 total_objs,页被拆回
# buddy 而页内仍有活对象(后续 free 只看到 magic=0 的旧页头)。
bi: int = _slab_bit_index(page, ptr)
if _slab_bit_test(page, bi) == 0:
_fd: [112]char
viperlib.snprintf(&_fd, 112, "[mm] slab free_obj: page=0x%lx ptr=0x%lx idx=%d not allocated - refused\n",
uint64(page), uint64(ptr), bi)
serial.puts(&_fd)
return
# 已全空的页也不能再收对象(计数/指针已不可信)
if page.free_count >= page.total_objs:
_fo: [96]char
viperlib.snprintf(&_fo, 96, "[mm] slab free_obj: page=0x%lx free=%d total=%d - refused\n",
uint64(page), int(page.free_count), int(page.total_objs))
serial.puts(&_fo)
return
_slab_bit_clear(page, bi)
_slab_write_ptr(ptr, page.free_head)
page.free_head = ptr
page.free_count += 1
if page.free_count == page.total_objs:
if page.prev:
page.prev.next = page.next
else:
sc: int = _slab_size_class(page.obj_size)
if sc >= 0:
self.caches[sc] = page.next
if page.next:
page.next.prev = page.prev
page.magic = 0
BuddySystemObject.free_block(page.block_ptr)
SlabAllocator: _SlabAllocator
# 内存分配函数
export def malloc(size: uint64) -> rawptr:
if size == 0: return None
if size <= SLAB_THRESHOLD:
sc: int = _slab_size_class(size)
if sc >= 0:
result: rawptr = SlabAllocator.alloc(sc)
if result is None:
# [DIAG] slab 分配失败(缓存链与建页都失败)
_mb: [96]char
viperlib.snprintf(&_mb, 96, "[mm] malloc(%lu) slab sc=%d -> NULL\n", size, sc)
serial.puts(&_mb)
return result
hdr: uint64 = memory_block.__sizeof__()
if size >= 4096:
total: uint64 = size + 4096
total = align_4k(total)
order: int = get_order(total)
if order > MAX_ORDER: return None
block: memory_block* = BuddySystemObject.allocate_block(total)
if not block: return None
user_ptr: uint64 = (uint64(block) + hdr + 4095) & ~uint64(4095)
real_hdr: uint64 = user_ptr - 8
__asm__(f"""mov rax, {asminp(block, ASM_DESCR.REG_ANY)}
mov [{asminp(rawptr(real_hdr), ASM_DESCR.REG_ANY)}], rax""",
op=[ASM_DESCR.CLOBBER_RAX, ASM_DESCR.CLOBBER_MEMORY])
return rawptr(user_ptr)
total2: uint64 = size + hdr
total2 = align_4k(total2)
order2: int = get_order(total2)
if order2 > MAX_ORDER: return None
block2: memory_block* = BuddySystemObject.allocate_block(total2)
if not block2: return None
return rawptr(uint64(block2) + hdr)
export def malloc_direct(size: uint64) -> rawptr:
if size == 0: return None
hdr: uint64 = memory_block.__sizeof__()
total: uint64 = size + hdr
total = align_4k(total)
order: int = get_order(total)
if order > MAX_ORDER: return None
block: memory_block* = BuddySystemObject.allocate_block(total)
if not block: return None
return rawptr(uint64(block) + hdr)
export def free(ptr: rawptr):
if ptr is None: return
sp: slab_page* = _slab_find_page(ptr)
if sp:
SlabAllocator.free_obj(ptr, sp)
return
# 加固:堆范围校验 —— 野指针(或已跑飞的指针)在动任何块头之前就报出来,
# 不然后面 `ptr-32` / `ptr-8` 取到的"块头"是随机内存,会把空闲链写坏。
_hs: uint64 = uint64(BuddySystemObject.buddy.start_addr)
_he: uint64 = uint64(BuddySystemObject.buddy.end_addr)
_hp: uint64 = uint64(ptr)
if _hp < _hs or _hp >= _he:
_fb2: [160]char
viperlib.snprintf(&_fb2, 160, "[mm] free(0x%lx) outside heap [0x%lx,0x%lx) - ignored\n", _hp, _hs, _he)
serial.puts(&_fb2)
return
if (uint64(ptr) & 0xFFF) == 0:
real_hdr: uint64 = uint64(ptr) - 8
block: memory_block*
__asm__(f"""mov rax, [{asminp(rawptr(real_hdr), ASM_DESCR.REG_ANY)}]
mov {asmout(block, ASM_DESCR.OUTPUT_REG)}, rax""",
op=[ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RAX])
BuddySystemObject.free_block(block)
return
block2: memory_block* = rawptr(uint64(ptr) - memory_block.__sizeof__())
BuddySystemObject.free_block(block2)
export def calloc(nmemb: uint64, size: uint64) -> rawptr:
total_size: uint64 = nmemb * size
ptr: rawptr = malloc(total_size)
if ptr:
string.memset(ptr, 0, total_size)
return ptr
export def realloc(ptr: rawptr, size: uint64) -> rawptr:
if ptr is None: return malloc(size)
if size == 0:
free(ptr)
return None
new_ptr: rawptr = malloc(size)
if not new_ptr: return None
sp: slab_page* = _slab_find_page(ptr)
if sp:
old_size: uint64 = sp.obj_size
elif (uint64(ptr) & 0xFFF) == 0:
real_hdr_r: uint64 = uint64(ptr) - 8
block_r: memory_block*
__asm__(f"""mov rax, [{asminp(rawptr(real_hdr_r), ASM_DESCR.REG_ANY)}]
mov {asmout(block_r, ASM_DESCR.OUTPUT_REG)}, rax""",
op=[ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RAX])
old_size = block_r.size - 4096
else:
block2: memory_block* = rawptr(uint64(ptr) - memory_block.__sizeof__())
old_size = block2.size - memory_block.__sizeof__()
copy_size: uint64 = old_size if old_size < size else size
string.memcpy(new_ptr, ptr, copy_size)
free(ptr)
return new_ptr
# 回收内存区域链表
def free_memory_regions():
global memory_regions
current: memory_region* = memory_regions
while current:
next_region: memory_region* = current.next
free(current)
current = next_region
memory_regions = None
# 初始化内存管理系统
def init(MemmapAddr: uint64, MemmapSize: uint64, MemmapDescSize: uint64, FbAddr: uint64 = 0, FbSize: uint64 = 0, KernelAddr: uint64 = 0, KernelEnd: uint64 = 0):
global BuddySystemObject
start_addr: rawptr = None
size: uint64 = 0
if MemmapAddr != 0:
if MemmapSize > 0:
if MemmapDescSize > 0:
num_entries: uint64 = MemmapSize / MemmapDescSize
max_memory: uint64 = 0
best_start: uint64 = 0
best_avail_size: uint64 = 0
best_avail_start: uint64 = 0
best_bs_size: uint64 = 0
best_bs_start: uint64 = 0
best_ld_size: uint64 = 0
best_ld_start: uint64 = 0
for i in range(num_entries):
entry: bootinfo.memory_map_entry* = bootinfo.memory_map_entry*(MemmapAddr + i * MemmapDescSize)
etype: uint64 = entry.type
start: uint64 = align_4k(entry.physical_start)
np: uint64 = entry.num_pages
end: uint64 = entry.physical_start + np * 4096
end = align_4k(end - 1)
region_size: uint64 = end - start
if FbAddr != 0 and FbSize != 0:
fb_end: uint64 = FbAddr + FbSize
if start < fb_end and end > FbAddr:
continue
# 排除内核镜像区([KernelAddr, KernelEnd),含 .bss):bootloader 只按
# 文件大小预留页,.bss 不占文件 → 其尾部留在 memmap 的 EfiConventionalMemory
# 里。若不排除,堆 arena 会用这段内存写 memory_block 头/用户数据,与内核
# 全局量(ticks/_perf_* 等)互相破坏,并进一步搞乱堆元数据。
# 内核镜像位于常规内存低端,故把与该区间重叠的 region 起点抬到镜像末端,
# 而不是整段丢弃(整段丢弃会连带弃掉镜像之上的一大片可用内存)。
if KernelEnd > KernelAddr:
if end <= KernelEnd:
continue
if start < KernelEnd:
# 抬高到页对齐(KernelEnd 未必 4KB 对齐):arena 起点必须页对齐,
# 否则伙伴系统的块地址全部错位。
start = align_4k(KernelEnd)
region_size = end - start
if end <= start:
continue
if etype == MEMORY_TYPE_AVAILABLE:
if region_size > best_avail_size:
best_avail_size = region_size
best_avail_start = start
if etype == MEMORY_TYPE_BOOT_SERVICES_DATA:
if region_size > best_bs_size:
best_bs_size = region_size
best_bs_start = start
if etype == MEMORY_TYPE_LOADER_DATA:
if region_size > best_ld_size:
best_ld_size = region_size
best_ld_start = start
min_blk: uint64 = MIN_BLOCK_SIZE
if best_avail_size >= min_blk:
best_start = best_avail_start
max_memory = best_avail_size
elif best_bs_size >= min_blk:
best_start = best_bs_start
max_memory = best_bs_size
elif best_ld_size >= min_blk:
best_start = best_ld_start
max_memory = best_ld_size
if best_start != 0:
if max_memory >= min_blk:
start_addr = rawptr(best_start)
size = max_memory
if start_addr is None:
_dsz: uint64 = detect_memory_size()
size = detect_memory_size()
size = align_4k(size)
# 起始地址 4MB(内核之后),避免超出物理 RAM
# 旧值 0x10000000 在 256MB QEMU 中超出物理 RAM 边界
start_addr = rawptr(align_4k(0x400000))
if size > 0x400000:
size = size - 0x400000
size = align_4k(size)
min_blk2: uint64 = MIN_BLOCK_SIZE
if size < min_blk2:
size = 33554432
BuddySystemObject.__init__(start_addr, size)
SlabAllocator.__init__()
# 虚拟内存映射结构
class vm_area:
VirtAddr: uint64
PhysAddr: uint64
size: uint64
flags: uint32
next: vm_area*
# 全局虚拟内存区域链表
vm_areas: vm_area* = None
# 博弈机制参数
define ALLOCATION_RETRIES = 5 # 分配重试次数
define FRAGMENTATION_THRESHOLD = 0.7 # 碎片率阈值
# 获取内存使用统计
def GetMemoryStats() -> tuple[uint64, uint64, uint64]:
return BuddySystemObject.buddy.total_memory, BuddySystemObject.buddy.used_memory, BuddySystemObject.buddy.free_memory
# 计算内存碎片率
def calculate_fragmentation() -> double:
# 计算空闲块的数量和总大小
free_blocks: int = 0
free_size: uint64 = 0
for i in range(MAX_ORDER + 1):
block: memory_block* = BuddySystemObject.buddy.free_lists[i]
while block:
free_blocks += 1
free_size += block.size
block = block.next
# 如果没有空闲内存,返回0
if free_size == 0:
return 0.0
# 计算碎片率
# 碎片率 = 1 - (最大空闲块大小 / 总空闲内存大小)
max_free_block: uint64 = 0
for i in range(MAX_ORDER, -1, -1):
if BuddySystemObject.buddy.free_lists[i]:
max_free_block = get_block_size(i)
break
if max_free_block == 0:
return 1.0
return 1.0 - (double(max_free_block) / double(free_size))
# 内存整理函数
static def compact_memory() -> int:
freed_pages: int = 0
for sc in range(SLAB_NUM_SIZES):
page: slab_page* = SlabAllocator.caches[sc]
while page:
next_page: slab_page* = page.next
if page.free_count == page.total_objs:
if page.prev:
page.prev.next = page.next
else:
SlabAllocator.caches[sc] = page.next
if page.next:
page.next.prev = page.prev
page.magic = 0
BuddySystemObject.free_block(page.block_ptr)
freed_pages += 1
page = next_page
return freed_pages
# 虚拟内存分配函数
def vm_alloc(size: uint64, flags: uint32) -> uint64:
global vm_areas
# 4K对齐
size = align_4k(size)
# 尝试分配物理内存,使用博弈机制
PhysAddr: rawptr = None
for retries in range(ALLOCATION_RETRIES):
PhysAddr = malloc(size)
if PhysAddr:
break
# 内存不足,尝试进行内存整理
if calculate_fragmentation() > FRAGMENTATION_THRESHOLD:
compact_memory()
if not PhysAddr: return 0
# 分配虚拟地址
# 简单的虚拟地址分配策略:从0x100000000开始
VirtAddr: uint64 = 0x100000000
current: vm_area* = vm_areas
# 寻找合适的虚拟地址空间
while current:
if VirtAddr + size <= current.VirtAddr: break
VirtAddr = current.VirtAddr + current.size
current = current.next
# 创建虚拟内存区域
area: vm_area* = malloc(vm_area.__sizeof__())
if area:
area.VirtAddr = VirtAddr
area.PhysAddr = uint64(PhysAddr)
area.size = size
area.flags = flags
area.next = vm_areas
vm_areas = area
# 映射虚拟地址到物理地址
# 计算需要映射的页数
pages: uint64 = size / 4096
for i in range(pages):
page_virt: uint64 = VirtAddr + (i * 4096)
page_phys: uint64 = uint64(PhysAddr) + (i * 4096)
# 调用paging模块的映射函数
paging.MapPage(page_virt, page_phys, flags)
return VirtAddr
# 虚拟内存释放函数
def vm_free(VirtAddr: uint64):
global vm_areas
# 查找虚拟内存区域
current: vm_area* = vm_areas
prev: vm_area* = None
while current:
if current.VirtAddr == VirtAddr:
# 取消虚拟地址映射
pages: uint64 = current.size / 4096
for i in range(pages):
page_virt: uint64 = current.VirtAddr + (i * 4096)
paging.UnMapPage(page_virt)
# 释放物理内存
free(rawptr(current.PhysAddr))
if prev:
prev.next = current.next
else:
vm_areas = current.next
free(current)
return
prev = current
current = current.next
# CMOS读取函数
def cmos_read(reg: uint8) -> uint8:
# in/out al 会改 EAX,必须 CLOBBER_RAX;显式 mov 到输出寄存器
#(与 asm.inb/inw 修复同模式,避免调用方 EAX 被静默破坏)。
__asm__(f"""mov eax, {asminp(uint32(reg), ASM_DESCR.REG_ANY)}
out 0x70, al""",
op=[ASM_DESCR.CLOBBER_RAX])
val32: uint32
__asm__(f"""in al, 0x71
movzx eax, al
mov {asmout(val32, ASM_DESCR.OUTPUT_REG)}, eax""",
out=[asmout(val32, ASM_DESCR.OUTPUT_REG)],
op=[ASM_DESCR.CLOBBER_RAX])
return uint8(val32)
# 内存大小测试函数
def detect_memory_size() -> uint64:
# 读取 1MB-16MB 扩展内存(KB 单位)
low: uint8 = cmos_read(0x15)
high: uint8 = cmos_read(0x16)
mem_kb: uint32 = (uint32(high) << 8) | low
# 读取 16MB 以上扩展内存(64KB 块为单位)
low2: uint8 = cmos_read(0x17)
high2: uint8 = cmos_read(0x18)
mem_above_16mb: uint32 = (uint32(high2) << 8) | low2
# 两个寄存器都为 0 时使用默认值
if mem_kb == 0 and mem_above_16mb == 0: return 0x10000000 # 256MB
# 计算总内存:1MB 基础 + 1MB-16MB + 16MB 以上
total_size: uint64 = 1048576 # 前 1MB
if mem_kb > 0:
total_size = total_size + uint64(mem_kb) * 1024
if mem_above_16mb > 0:
total_size = total_size + uint64(mem_above_16mb) * 65536
total_size = align_4k(total_size)
# 确保内存大小至少为32MB
if total_size < 33554432: # 32MB
total_size = 33554432
return total_size