汇编:没有 malloc 和系统调用的动态内存分配? [FreeDOS 应用程序]
Posted
技术标签:
【中文标题】汇编:没有 malloc 和系统调用的动态内存分配? [FreeDOS 应用程序]【英文标题】:Assembly: dynamic memory allocation without malloc and syscalls? [FreeDOS application] 【发布时间】:2019-11-10 00:01:32 【问题描述】:我的问题是关于汇编中动态内存分配的逻辑(特别是 MASM)。关于这个主题有很多文章,它们都依赖于 malloc 或 brk 的使用。但是,根据我的理解,作为 C 语言一部分的 malloc 必须(或可以)肯定是用汇编编写的。 brk 的同义词,因为它是操作系统的一部分,因此也是用 C 编写的,可以通过汇编进行 1 对 1 的替换。很久很久以前,我在 PCMag 上看到一篇关于在 MS-DOS 中使用纯 asm 进行动态内存分配的文章。不幸的是,我已经失去了这篇精彩作品的所有痕迹。现在我正在使用 FreeDOS(可精确启动的 FreeDOS 闪存卡),并且想知道如果有人决定编写自己的内存分配器,该怎么办?不依赖操作系统机制的内存分配的出发点和逻辑是什么?
【问题讨论】:
您的“纯 asm”示例可能依赖于中断或其他东西。要么,要么他们分配自己的大型静态内存块,并将其用作他们的堆,从中分配他们自己的“动态”块。程序集有一个非常静态的内存视图,因此您的自定义 malloc 实现将依赖于某种系统调用/中断,或者只是作为目标文件(BSS/数据段)的一部分分配的大型静态内存块 你在这里缺少的是,如果你不使用 FreeDOS 的分配器,你将不知道 FreeDOS(和其他东西)已经分配了哪些内存,而 FreeDOS 也不知道你的内存是多少已分配。您可以做的是使用 FreeDOS 分配一大块内存,然后使用您自己的分配器对其进行子分配。请注意,您不能只自己编写 brk(),因为在 Unix 类型的系统上,它是一个将内存映射到进程的系统调用,这需要在内核中完成。换句话说,要在最低级别执行内存分配,您需要编写自己的操作系统。 @prushik :不,您不一定可以访问所有内存,除非您想破坏 MS-DOS 和其他应用程序,使系统无法使用。通常 DOS 程序会为 HEAP 操作请求超出程序最低要求的额外内存。顺便说一句,我最近写了一个有关加载时 MS-DOS 分配的一些相关的 SO 答案。 ***.com/a/56747280/3857942 。你可以为你的堆请求可用空间,然后你写一个分配器(malloc/free)来使用你从 MS-DOS 请求的内存块。 因为中断都是由 Bios 提供的 - 不,DOS 系统调用的 ABI 是int 21h
和 AH= call number。 BIOS 使用几个不同的中断号,但它不是唯一可以通过软件中断调用的东西。
当然,DOS 中的程序在编写时都知道它们被分配了哪些内存。所有程序都可以在任何地方编写,但为了发挥功能,它们通常会尝试相互配合。这通常意味着不要随意走遍 MS-DOS 和其他应用程序。你想要你请求的内存。你的内存太多了,你还给它。
【参考方案1】:
当 DOS 加载一个 .COM 程序时,它会将 640KB 区域(0a000h:00000h 以下)的所有可用内存分配给程序,程序可以管理自己的内存。如果希望使用 MSDOS 内存管理,程序首先必须使用INT 21H, AH=49H, ES=segment, BX=# paragraphs
释放内存。然后它可以使用INT 21H, AH=48H, BX=# paragraphs
来分配内存。
如 cmets 中所述,.EXE 程序可能会也可能不会分配 640KB 区域中的所有内存。
示例 .COM 汇编代码,用于释放,然后分配所有可用内存。 MSDOS 通常会消耗 16 个字节的开销。在此示例中,BX
设置为代码末尾,然后设置为下一段边界,即代码末尾之后 256 个字节以用作堆栈空间。此堆栈的末尾是INT 21H, AH=4AH
调用释放的内存的基数。
.286
.model tiny,c
.code
org 0100h
; cs,ds,es,ss = program segment prefix, sp = 0fffeh
start: mov bx,offset cdend ;set bx=end stack
add bx,0010fh
and bx,0fff0h
mov sp,bx ;sp = new end of stack
mov cl,4 ;release memory
shr bx,cl
mov ax,04a00h
int 21h
mov ax,04800h ;set bx = available memory
mov bx,0ffffh
int 21h
mov ax,04800h ;allocate all of it
int 21h ; returns segment in ax
exit: mov ax,04c00h ;exit
int 21h
cdend:
end start
【讨论】:
EXE 程序存在例外情况,即在加载时不会请求分配所有内存。这取决于用于生成可执行文件的工具或 EXEMOD 程序的使用。碰巧许多工具使用 MAXALLOC 值 0xffff 具有分配所有内存的效果。 COM 程序虽然总是被分配所有的内存。内存的限制也有所不同,并且由于各种原因可能高于或低于 A000。为程序分配内存后的段落可以在 PSP 中的偏移量 0x0002 处找到 在这个 SO 答案中讨论了其中一些事情:***.com/a/56747280/3857942 @MichaelPetch:我认为这个问题需要一种“存根”答案,以解决问题中明显的误解(您可以忽略 DOS 并且不进行任何 DOS 系统调用)并链接到您的答案完整的细节。它不是完全重复的。 MS-DOS 也只分配可用的最大空闲内存块。虽然通常不太可能,但可能存在现有分配使传统内存碎片化。更有可能的是,EBDA 可以使用传统内存末端的内存。 TSR 和设备驱动程序有时也会在常规内存的末尾分配内存以避免碎片。 @ros-s-ridge : DOS 做的另一件事...如果程序在 EXE 的 MZ 标头中有 MINALLOC=MACALLOC=0 DOS 将尝试将程序加载到可用内存的顶部,而不是比底部以上是关于汇编:没有 malloc 和系统调用的动态内存分配? [FreeDOS 应用程序]的主要内容,如果未能解决你的问题,请参考以下文章