六、Linux汇编——内存

一、汇编程序在内存中的布局

    一、计算机中寻址单位为“1字节”,而操做单位为“1个字”,32bit机中,1个字有包含4字节。编程

    二、程序中的每一个.section都被加载到不一样的内存区,尽管程序中,不一样的.section可能被隔离,可是在内存中,各段将会被组合起来,填充至连续的内存区域。函数

    三、.text段(程序段)的起始地址为0x08048000,其后是.data段,最后是.bss段。oop

    四、Linux上可寻址的最后逻辑位置是0bfff ffff ,栈今后处开始,栈顶向低地址移动。从高地址开始,栈的布局以下:布局

        (1)栈底,存在一内存字0spa

        (2)以ASCII表示的,空字符结束的程序名操作系统

        (3)环境变量命令行

        (4)程序的命令行参数,由用户输入指针

        (5)用到的参数数量code

    程序开始时,栈指针从低地址向高地址移动。内存

    五、程序的数据段移动方向为:低地址——>高地址;栈的移动方向是:高地址——>低地址。在程序段和栈段之间的区域用户程序没法访问,由操做系统维护,若尝试访问,则提示“segment fault”——段错误;若访问比0x0804800低的内存,获得一样错误。

    六、对于程序段,其最后可访问的内存地址成为“系统中断”(或当前中断,中断)。以下图为程序在内存中的构造状况:


二、逻辑地址映射机制

    每一个程序都存放在逻辑地址空间:0x08048000~0xbfff ffff,当程序真正加载到内存是,操做系统将分配一个真实的物理地址空间,与逻辑空间产生对应。上面的“中断”是未映射内存区的起始部分。

    同时,因为内存容量有限,因此程序的一部分并不在内存中,而是存储在磁盘上。下面描述了Linux的内存访问处理方式:

    (1)程序尝试从虚拟内存加载数据

    (2) 将逻辑地址转换成物理地址

    (3)若不在内存,则从磁盘加载数据

    (4)若内存空间不足,则换出程序,更改映射表

    (5)恢复程序控制权,处理指令

为了让上述操做更高效,操做系统将内存分为若干页,每次内存分配、交换、映射都以页为单位。

三、获取更多内存

    为了在容许的寻址空间内使用更多内存,由上面的叙述可知,须要移动“中断点”,从而实现内存的“动态分配”。新分配的内存空间是“当前中断点”和“新中断点”之间的连续内存空间。能够经过brk系统调用来实现内存的分配,借口以下:

brk系统调用:系统调用号:%eax=45                      

                        请求的中断点:%ebx

                        中断调用:int %0x80

                        返回值:若内存不够,则%eax=0;若%ebx=0,该调用仅返回最后一个可用的内存地址。

下面程序实现了简单的内存分配与回收。

#目的:管理内存使用——按需分配和释放内存
#内存头:新分配的内存有一个meta区,存放如下信息:available/unvailable,内存大小,实际内存位置
#        为方便程序调用,返回的内存地址仅存储请求的内存的实际位置

.section .data
#全局变量
    .heap_begin:    .long 0  #管理的内存的起始处
    .current_break: .long 0  #当前中断点,为管理的内存以后的位置
    
#结构信息
    .equ HEADER_SIZE, 8         #内存头空间大小
    .equ HDR_AVAIL_OFFSET, 0    #头中,available标志的位置
    .equ HDR_SIZE_OFFSET, 4     #头中,存储内存空间大小的位置
#常量
    .equ UNAVAILABLE,0    #标记空间已分配
    .equ AVAILABLE, 1     #标记空间未分配
        
    .equ SYS_BRK, 45 
    .equ LINUX_SYSCALL, 0x80
    
.section .text
  
#函数1:滴用此函数初始化——设置heap_begin和current_break,此函数无参数和返回值
.globl allocate_init
.type allocate_init, @function
    allocate_init:    pushl %ebp
                      movl %esp, %ebp
                      
                      #若发起brk系统调用时,%ebx为0,该调用返回最后一个可用的地址
                      movl $SYS_BRK, %eax     #肯定中断点
                      movl $0, %ebx
                      int $LINUX_SYSCALL      
                      
                      incl %eax             #%eax为最后有效可用地址,此条指令得到“中断”的位置
                      movl %eax, current_break #保存当前中断
                      movl %eax, heap_begin    #将当前中断保存为首地址,
                      
                      movl %ebp, %esp
                      popl %ebp
                      ret
#函数2:此函数用于获取一段内存,其首先查看是否有自由内存块,若不存在,则想Linux请求空间
#参数:内存块大小
#返回值:将分配的内存大小返回给%eax, 若无空间可分配,%eax=0
#变量:    %ecx——保存请求的内存大小
#         %eax——检测当前内存区
#         %ebx——当前中断位置
#         %edx当前内存区大小
#程序流程:检测heap_begin开始的内存区,若内存区大于请求的内存大小,则可用;
#若没法找到足够大的内存,则向Linux申请内存,函数移动current_break
.globl allocate
.type allocate, @function 
.equ ST_MEM_SIZE, 8         #分配内存大小的栈位置
    allocate:    pushl %ebp
                 movl %esp, %ebp
                 
                 movl ST_MEM_SIZE(%ebp), %ecx  #%ecx保存须要的内存大小
                 movl heap_begin, %eax         #%eax保存当前搜索起始位置
                 movl current_break, %ebx      #%ebx保存当前中断
     alloc_loop_begin:    #循环搜索每一个内存区
                         cmpl %ebx, %eax     #二者相等,则须要更多内存
                         je move_break
                         
                         #当前内存区足够用
                         movl HDR_SIZE_OFFSET(%eax), %edx    #获取当前内存区大小
                         cmpl $UNAVAILABLE, HDR_AVAIL_OFFSET(%eax)    #当前内存区不可用
                         je next_location     #寻找下一个内存区
                         
                         cmpl %edx, %ecx    #若内存区可用
                         jle allocate_here    #%ecx(须要使用的内存空间) <= %edx(当前可用的内存空间)
                                              #空间足够使用
       
      next_location:    addl $HEAD_SIZE, %eax #内存区总大小为:所需大小(存储于%edx)+内存头8bytes
                        addl %edx, %eax        #得到下一个存储区的首地址
                        jmp alloc_loop_begin    #查看下一个内存区
                        
      allocate_here:    #分配的内存区头在%eax中,空间可用
                        movl $UNAVAILABLE, HDR_AVAIL_OFFSET(%eax)
                        #设置内存大小
                        movl %ecx, HDR_SIZE_OFFSET(%eax)
                        addl $HEADER_SIZE,%eax   #移动到内存的起始处
                        
                        movl %ebp, current_break    #保存新中断
                   
                        movl %ebp, %esp
                        popl %ebp
                        ret
   error:    movl $0, %eax    #若是出错,则返回0
             movl %ebp, %esp
             popl %ebp
             ret
             
#deallocate
#功能:将用完的内存区返回给内存池
#参数:将被返回给内存池的内存地址
#具体处理:处理分配出来的内存区的8bytes的头部便可
.globl deallocate
.type deallocate, @function

.equ ST_MEMORY_SG, 4   #要释放的内存区域栈位置

deallocate:    #因为未将%ebp入栈,故此处使用4(%esp)
                movl ST_MEMORY_SEG(%esp), %eax
                subl $HEADER_SIZE, %eax  #得到指向内存实际起始处的指针
                movl $AVAILABLE, HDR_AVAIL_OFFSET(%eax) #标识该内存区可用
                ret  #返回

   因为内存管理器并不是完整的程序,所以其无入口函数(_start) 。

编译:as alloc.s -o alloc.o

相关文章
相关标签/搜索