一、计算机中寻址单位为“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