六、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

相關文章
相關標籤/搜索