一、計算機中尋址單位爲「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