X86汇编语言-从实模式到保护模式—笔记(32)-第13章 程序的动态加载和执行(10)
本帖最后由 兰陵月 于 2017-12-5 21:50 编辑(五)动态分配内存在流行的操作系统里,内存管理是一项重要而又严肃的工作,相当复杂。它要记住所有可以分配的内存,将它们分块。这样,当要求分配内存时,内存管理程序将查找并分配那些大小相符的空闲块;当占用这些块的用户终止执行后,还要负责回收它们,以便再用于分配;当内存空间紧张,找不到空闲块,或者空闲块的大小不能满足需求时,内存管理程序还要负责查找那些很少被访问的块,将其中的数据移到硬盘上,腾出空间来满足当前的需求。下次当这些块再次被用到时,再用同样的办法从硬盘调回内存。第412行已经将实际需要的内存数量计算出。第414行,EAX中存放着加载用户程序实际需要的内存数量(以字节计),将EAX的值传送给寄存器ECX,ECX将作为过程allocate_memory的参数。第415行,调用系统公用例程段的过程allocate_memory。该过程位于第232行。过程的作用是用来分配内存。参数为ECX,希望分配的字节数。返回值为ECX,分配好的内存的起始线性地址,allocate_memory只是分配内存,并未把程序从硬盘加载到此处,所以此处返回的ECX将作为加载程序的目的地址。来到第232行,程序进入分配内存过程;第235~237行,压栈保护过程中要用到的相关寄存器;第239、240行,切换DS到内核数据段;第242行,有一个标号ram_alloc,它在第335行声明。值为0x00100000,注释为下次分配内存时的起始地址,它是每次调用过程allocate_memory时分配的内存空间的起始地址,因此在本次调用过程结束处,我们还要指定下一次要分配的内存空间的起始地址,将它写回标号ram_alloc处;第243行,将0x00100000与传入的参数ECX的值相加,ECX的值我们知道,它是希望分配的字节数。因此本条指令执行之后,EAX的值指向x00100000+ECX处,我们可以理解这是加载完用户程序后紧邻着空白地址处,也就是说,下一次如果还要为某个程序分配内存,我们就可以从0x00100000+ECX处开始分配。这就是注释里“下一次分配时的起始地址”的意思,EAX的内容在第254行将被回填至标号ram_alloc处。第247行,将标号ram_alloc所在地址的内容0x00100000传送给寄存器ECX。这样ECX就放置着本次分配内存空间的起始地址。第249~第253行的工作主要是将下次拟分配的内存空间起始地址强制性4字节对齐,所谓4字节对齐的意思就是,这个地址必须能够被4整除,能够被4整除的数字有一个特征,那就其最后两位都是“0”,4字节对齐的好处是访问速度最快;第249行,将下次拟分配的内存空间起始地址值传送给寄存器EBX,作备份。第250行,假如EBX的值不能够被4整除,那么将起始地址值的低2位强制性归零,这样得到的EBX就能够被4整除了,但是这样就会导致地址值数值变低,这样会影响前面已经分配的内存空间。比如EBX寄存器中装有地址值0xD4E92D36,执行and ebx,0xfffffffc后,寄存器EBX的值变为:0xD4E92D34,假如上次分配的内存空间在0xD4E92D35处结束,这样就有一个字节被抛弃了。因此,第251行,将ebx的值再加上4,这样,无论如何都不会影响上一次分配的内存空间,且地址值也能够被4整除。但是,假如地址本身就能够被4整除,那就没必要进行第250、251行的操作。所以,第252行,将EAX寄存器的值与0x00000003进行比较,看最低两位是否全为零。前面操作的EBX中的值只是EAX中的备份,它们的值都是一样的,都表示下次拟分配的内存空间的地址。第253行,如果eax的最低2位都是零,表明这个地址值本身就能够被4整除。则test执行后,ZF值为1,那么指令cmovnz就不会执行。如果eax的最低2位不全为零,表明这个地址值不能够被4整除。则test执行后,ZF值为0,cmovnz指令就会执行,它将EBX的值(修正后的能够被4整除的地址值)传送给寄存器EAX。第254行,程序执行到这里,寄存器EAX的值肯定是能够被4整除了,将它回填到标号ram_alloc处。第256~258行,恢复相关寄存器的值。第260行,retf,程序远程返回,远返回的过程,也只能远调用。(六)从硬盘读取用户程序到指定的目标地址我们再返回到过程load_relocate_program过程。第415行,调用allocate_memory过程分配内存空间之后,该过程返回了一个已分配的内存空间的起始地址,在寄存器ECX中;第416行,将该起始地址传送给寄存器EBX;第417行,将寄存器EBX压栈保存;第418行~第421行,将实际需要的内存数量(此数值已经经过处理,与整数倍的用户程序长度相对应)除以512,得出需要从硬盘上读取的用户程序的扇区总数,将该扇区总数给寄存器ECX,作为读取硬盘的循环次数。第423、424行,切换DS到可以访问整个4GB内存空间的数据段;第426行,将ESI的值(用户程序在硬盘上的起始逻辑扇区号,这个值在调用过程load_relocate_program后一直未改变)传给寄存器EAX;第427~第430行,循环体,用于读取用户程序。每读取一个扇区,将寄存器EAX自加1,指向下一个扇区,直到用户程序全部读取完毕。
页:
[1]