我目前正在编写一个bootloader并且已经开始耗尽我的512B空间,所以我开始在512B之外编写更多代码并打算使用bootloader将其读入内存。我添加到我的代码的末尾:
stack_start:
resb 4096
stack_end:
这样我就可以在操作系统代码的末尾为堆栈分配空间。目前在我的引导程序中,我使用以下here中的以下内容在引导加载程序之后为堆栈分配4KiB:
mov ax, 07c0h ; 4K stack space after the bootloader -- code is running at 0x07c0
add ax, 288 ; (4096 + 512)/16 bytes per paragraph (288 paragraphs)
mov ss, ax
mov sp, 4096 ; moves the stack pointer
但是现在我需要一种方法来将堆栈分配到我的操作系统代码的末尾,这将是一个未知的大小。
我相信我理解这些是如何设置的 - 类似于使用es
,我使用ss
来扩展寻址空间,但我找不到任何可以解释它的东西因为我的知识水平。我也不确定如何正确设置它以使我的堆栈结束。我用过:
mov ax, stack_start
mov ss, ax
mov sp, 4096
并没有遇到错误;但是我想知道这是否正确,或者我是否真的只为堆栈分配了一些空间,同时用更高的地址填充内存。
ss
如何实际运作?我如何使用它和sp
在我的代码末尾为堆栈分配内存?
这是在使用nasm的i386中。
编辑:如果可能的话,验证堆栈位于正确位置的某些方法也非常有用。
答案 0 :(得分:3)
我知道这对初学者来说是可怕的,但是只要满足要求并且没有错误,就没有好的或错误的方法来设置堆栈。
在拥有一个完全正常工作的内存管理器之前,你(人)必须是内存管理器
你必须知道你如何使用记忆
"分配内存的整个概念"还不存在!你没有内存管理器,你只有一堆RW RAM地址。
首先要求引导程序可以安全地假设的最小内存量 由于初始程序加载器(引导加载程序)位于0x7c00,因此可以认为系统具有31.5KiB的内存。 您还可以假设没有内存,并依赖缓存,但这是高级主题。
当出现问题时,陈述假设至关重要。
然后您必须了解保留和使用区域,这是通过标准memory map实现的。
摘录:
00000 - 003ff IVT
00400 - 004ff BDA
00500 - 0052f Dangerous Zone (The Petch Zone :) )
00530 - 07bff Free
07c00 - 07dff IPL
" Petch Zone" is an inside joke并向Michael Petch致敬。
通过设置临时堆栈来建立您的最小环境
在上面的片段中,区域00530 - 07bff
是免费的,您可以将它用作~29KiB堆栈
由于堆栈是完全降序的,因此您可以将堆栈指针ss:sp
放在07c00
07c00
是物理地址,将其转换为任何合适的逻辑地址(0000:7c00
,0001:7bf0
,0002:7be0
,0003:7bd0
,. ..,07c0:0000
,任何人都会选择你最喜欢的那个)并以任何原子方式将SS:SP
设置为它中断你知道/喜欢。
编辑如果正确注意到Ped7g的偏移下溢从0到fffe
,则使用带有小偏移部分的逻辑地址会导致问题。
虽然静态行为检查(起始地址相同),但动态行为失败,因此最好使用0000:7c00
,而且肯定不会使用0053
以上的段。
任何其他区域都可以,将堆栈指针设置为a0000
(传统内存的末尾)是另一种选择。
没有比这更好的了,只是意识到你把它放在哪里。
编辑:评论中的Michael Petch pointed out为a0000
,地址9c000
也很危险。
更安全的地址是00000 - 003ff IVT
00400 - 004ff BDA
00500 - 0052f Dangerous Zone (The Petch Zone :) )
00530 - 07bff Stack
07c00 - 07dff IPL
07e00 - 08fff Kernel
09000 - 10000 Other kernel stuff
。
使用符合您需要的块更新内存映射 记下内核开始和结束的位置,动态数据所在的位置等等。
例如
malloc
到目前为止,您可能已经在内存映射中使用了静态块,但如果您想使用超过1 MiB的内存,则需要query the BIOS来获取可用内存的内存映射。
这本质上是动态的,因为每个系统都有不同的内存量。
该映射只是内存管理器的一个非常小的元数据,所以现在是时候......
在您设置到目前为止的基本环境中,编写一个简单内存管理器,该书可以保存内存块。
陷阱:内存管理器需要一些"元内存"保留其已分配内存的书,这需要假设。
一旦你可以分配和释放内存,你可以将堆栈移动到更大的区域,从磁盘或等效的方式加载其他数据等等。
我们的想法是,您现在可以像使用mfree
和String?
一样动态管理内存,从而减轻您在心理上处理内存映射的负担。
更高级的内存管理器通常用高级语言编写,数据操作更容易(特别是在处理Paging等主题时)。
答案 1 :(得分:1)
不要将堆栈放在代码的 end ,而是将其放在开头。这实际上是启动加载器的常见做法。换句话说:
cli
mov ax,0x7c00
mov sp, ax
xor ax, ax
mov ss, ax
sti
您现在正在从您的装载程序代码加载的位置开始增长。这也使事情变得更容易,因为您要做的下一件事之一就是加载第二阶段。这使您可以轻松地在同一个程序集文件中创建第二个阶段,加载并跳转到它,而不必担心堆栈覆盖代码。
答案 2 :(得分:1)
我在启动代码中做的一件事就是尽可能将堆栈放在常规内存中。
Select t2.Name, t2.HRS_TOT, t2.APPROVE_DATE, t1.Date, t1.HRS FROM t1 INNER JOIN t2 On t1.SumOfHRS = t2.HRS_TOT t1 = SELECT Name, Date, Sum(HRS) As SumOfHRS, FROM tbl1 GROUP BY date ORDER BY ID
SYS_MEM equ 12H
StkSize equ 44 ; # of 1024 byte blocks to reserve for stack
这里有一些我的要求特有的东西,但基本上我在内存顶部抓住了44k,这就是堆栈的存在,直到我进入64位。
然后,在我的情况下,读取内核的代码被移动到540H并且内核( 1181扇区)被读入1000H到94600H。我发现这在实际和受保护模式下非常实用。