资源预览内容
第1页 / 共12页
第2页 / 共12页
第3页 / 共12页
第4页 / 共12页
第5页 / 共12页
第6页 / 共12页
第7页 / 共12页
第8页 / 共12页
第9页 / 共12页
第10页 / 共12页
亲,该文档总共12页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述
内存管理知识总结 -王晶晶一:概念总结: 虚拟内存和物理内存的区别,关系和映射: 每个进程都有一个虚拟内存,当一个进程获得CPU 运行时,它的虚拟内存就被CPU 所 知,但是根据虚拟内存,CPU是无法执行该进程的,CPU必须知道其相应的物理内存的地 址才能运行该进程。 所以在虚拟内存和物理内存之间就需要建立一个映射关系。而这个关系 是通过页表来实现的。 所以, 一个进程它不但对应一个自己独立的虚拟内存, 它还要有一个 属于自己的页表。所以当进程发生切换时,虚拟内存和页表也要发生切换。 那在切换之前该进程的虚拟内存和页表是存放在磁盘中的,所以转换的时候就是将该进 程的虚拟内存和页表从磁盘切换到内存。物理内存的大小是有限的,但是CPU 一次只能执 行一个进程,所以物理内存的大小来应付一个进程的还是可以的,所以对于其他暂不执行的 进程就将它们的虚拟内存地址存放在磁盘上。如果一个进程所需的物理内存过大, 可以分段 为其分配物理内存,即执行到哪段内容就为其分配物理内存。 可以这么说:虚拟内存的引入就是为了补充物理内存空间不足的问题。实现方式就是: 先给进程分配一个虚拟内存, 将其存放在磁盘上, 由于CPU 在同一时刻只会执行一个进程, 所以这些进程的虚拟内存地址之间不存在任何的冲突, 即为它们分配的虚拟内存地址可以是 一样的。运行哪个进程的时候就将其虚拟内存调入内存,根据其虚拟内存的地址和页表得出 其物理地址,此时CPU 是不管其它进程的虚拟内存,而且这些虚拟内存此时对CPU 是不可 见的, 因为它们现在都存放在磁盘上。 当该进程执行完后, 就将其换出, 将下一个进程换入, 现在物理内存就又归这个进程。而之前那个进程在物理内存中的内容已经换出,存放到磁盘 上,所以即便时同一段物理内存,都不同的进程来说,其内容并不冲突。 在书上有这么一段话: 每个进程都有自己的似有用户空间(0-(3G-1)B),这个空间都其他进程是不可见的。 最高 的1GB的内存空间(内核空间)则由所有进程及内核共享。 任意时刻,在一个CPU 上只有一个进程在运行,所以对此CPU 来说,在这一时刻,只 存在一个虚拟的4GB 的内存空间, 这个虚拟地址空间是面向此进程的。 当发生进程切换是, 虚拟地址空间也随着切换。由此可以看出每个进程都有自己的虚拟地址空间, 只有此进程运 行的时候,其虚拟地址空间才被运行它的CPU所知。在其他时刻,其虚拟地址空间对CPU 来说是不可知的。所以,尽管每个进程都可以有4GB的虚拟地址空间,但在CPU眼中,只 有一个虚拟地址空间存在。虚拟地址空间随进程的切换而变化。 例:一个进程从其用户空间的地址0x1234ABCD处读取整数8,而另外一个进程从其 用户空间的地址读取整数20,这取决于进程自身的逻辑。二:进程的用户空间: 进程的用户空间从上到下分为:堆栈段,空洞(动态内存或堆),数据段,代码段。 数据的存储区分为:静态存储区,堆区(动态内存分配区)和栈区(堆栈区) 。 静态存储区:内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都 存在,它主要存放静态数据,全局数据和常量(包括字符常量)。 栈区: 在执行函数时, 函数内局部变量的存储单元都可以在栈上创建, 函数执行结束时, 这些存储单元被自动释放。 堆区:也称为动态内存分配。程序在运行时用 malloc 或 new申请任意大小的内存, 程序员自己负责在适当的时候用free 或delete来释放申请的内存。 动态内存的生命周期由程序员自己决定,但如果程序员忘记了释放内存,那么程序将在最后才释放掉动态内存。 上述所说的静态存储区就对应进程用户空间中的堆栈段,堆区就对应空洞(动态内存或 堆),栈区对应数据段。栈区堆区静态存储区三:相关数据结构简介(为了看起来清晰点所以就先贴到博客园里面,再贴图过来,所以图 代码被分成了好几个图片,看起来可能不是很连续): mm_struct: 源代码:堆栈段空洞数据段代码段每个进程的虚拟内存由 mm_struct结构来代表,该结构包含了当前执行影响的信息,并且 包含了一组指向vm_area_struct 结构的指针,vm_area_struct结构描述了虚拟内存的虚拟区, 该虚拟区值得就是上述用户空间划分的四个虚拟区, 这四个虚拟区在vm_area_struct 中通过vm_next 指针来链接。 部分数据结构成员简介: mmap:指向虚拟区vm_area_struct 结构的单链表的头。 mm_rb:指向虚拟区的红黑树 mmap_cache:将最近一次用到的虚存区存放到告诉缓存,mmap_cache 指向该虚存区 *get_unmapped_area:该函数指针指向在进程空间搜索虚拟区的函数。 *unmap_area:释放虚拟区的函数指针 mmap_base:标识第一个分配的匿名虚拟区或内存文件映像的线性地址 pgd:进程的页目录基地址。 mm_users:存放共享该数据结构的轻量级进程的个数 mm_count:内存描述符的计数器,每次mm_count递减的时候,都要检查其值是不是0, 如果是就要解除这个内存描述符,因为已经没有用户进程在使用它。 map_count:虚存区的个数 semaphore:对mm_struct结构进行串行访问所使用的信号量 start_code,end_code,start_data,end_data:进程的代码段和数据段的起始地址和结束地 址 start_brk,brk:空洞(堆)的起始地址和结束地址 start_stack:堆栈段的起始地址 arg_start,arg_end:命令行参数在堆栈部分的起始地址和终止地址 env_start,env_end:环境字符串所在堆栈部分的起始地址和终止地址 mmap:vm_area_struct 虚存区结构形成的单链表,其基地址由小到大排列 mmap_avl:vm_area_struct虚存区结构形成的 AVL平衡树vm_area_struct 数据结构:描述进程用户空间的一个虚拟内存空间 源代码:部分结构体成员简介: vm_mm:指向虚存区所在的mm_struct 结构的指针 vm_start,vm_end:虚存区的起始地址和终止地址 vm_page_prot:虚存区的保护权限 vm_flags:虚存区的标志,即VM_READ,VM_WRITE,VM_EXEC(rwx) vm_next:构成线性链表的指针,按虚存区基址从小到大排列 vm_avl_height,vm_avl_left,vm_avl_right:这 三 个 域 一 起 构 成 了 AVL 树 , 其 中 vm_avl_height是该节点距根节点的高度,vm_avl_left和vm_avl_right分别是该节点的 左右两个子树 vm_ops:对虚存区进行操作的函数。给出可以对虚存区中的页所做的操作。 Vm_pgoff:虚存空间起始地址在所描述文件里面的文件偏移,单位为物理页面 vma(虚存区)是可以被共享和保护的独立实体。 引入 vm_area_struct结构的原因是: 进程的虚存区通常是不连续的,而且不同的虚存区的属性不同, 所以一个进程的虚存区需要 多个 vm_area_struct数据结构来描述。 在vm_area_struct 中引入AVL 平衡树的原因是:在虚存区数目较少的时候,用单链表通 过vm_next 指针来链接,以基地址升序的顺序来排列,但是当虚存区过多时使用这种方法 来链接各虚存区,效率就会受到影响,所以采用AVL平衡树来解决这个问题。四,mmap和 munmap函数的简介: 头文件为#include mmap 函数原型:void* mmap(void*start, size_t length, int prot, int flags, int fd, off_t offset);功能:将一个文件或者其他对象映射到内存,返回其映射在内存的起始地址。参数说明:start: 是映射地址, 指向欲对应的内存起始地址, 通常设置为NULL, 由系统自动选定地址,对应成功后,该地址以返回值返回。length:是映射长度,文件中多大的部分映射到内存。prot:是映射保护权限, 可以是PROT_EXEC, PROT_READ, PROT_WRITE, PROT_NONE (页不可访问) ,这 4种权限可以结合使用。flags:是指映射类型, 可以是MAP_FIXED, MAP_PRIVATE, MAP_SHARED, 该参数必须 被指定为MAP_PRIVATE和MAP_SHARED其中之一, MAP_PRIVATE 是创建一个写时拷贝 映射(copy-on-write), 也就是说如果有多个进程同时映射到一个文件上, 映射建立时只是 共享同样的存储页面, 但是某进程企图修改页面内容, 则复制一个副本给该进程私用, 它的 任何修改对其它进程都不可见.而MAP_SHARED 则无论修改与否都使用同一副本, 任何进 程对页面的修改对其它进程都是可见的.其中, 如果flags 的MAP_FIXED不被置位, 则该参 数通常被忽略。fd:是映射文件的文件描述符offset:是映射文件中的偏移地址,通常设置为0,代表从文件的开始处开始映射,offset 必须是分页大小的整数倍。返回值说明:映射成功则返回映射区的内存起始地址,否则返回MAP_FAILED(-1) 。一般情况下将返回之强制转换为 char*,然后就可以对其执行有关字符串的操作,当然在 这之前,先要在该字符串的最后加上/0,对应的程序段为:fd = open(argv1, O_RDWR |O_CREAT, S_IRUSR | S_IWUSR); flength= lseek(fd, 1, SEEK_END); /* 在文件最后添加一个空字符,以便对其进行字符串的操作 */ write(fd, “0“, 1); lseek(fd, 0, SEEK_SET); /允许读,不允许其它进程访问此内存区域 mapped_mem = mmap(start_addr, flength, PROT_READ, MAP_PRIVATE, fd, 0);munmap 函数原型: 功能:删除文件或者其他对象在内存中的映射。 int munmap(void *start, size_tlength);参数说明: start:文件在内存中映射的起始地址; length:文件映射在内存中的长度。 函数返回值:成功返回-1。出错返回 errno,其值被设为以下的某个值:EACCES:访问出错EAGAIN:文件已被锁定,或者太多的内存已被锁定 EBADF:fd不是有效的文件描述词 EINVAL:一个或者多个参数无效 ENFILE:已达到系统对打开文件的限制ENODEV:指定文件所在的文件系统不支持内存映射 ENOMEM:内存不足,或者进程已超出最大内存映射数量 EPERM:权能不足,操作不允许 ETXTBSY:已写的方式打开文件,同时指定MAP_DENYWRITE 标志 SIGSEGV:试着向只读区写入 SIGBUS:试着访问不属于进程的内存区一般情况下内存的映射步骤: 1. 用open函数打开文件,并返回文件描述符; 2. 用mmap 建立内存映射,并返回映射首地址 start 的指针; 3. 对文件的映射进行操作,例如printf,sprintf. 4. 用munmap(void*start, size_t length)关闭内存映像; 5. 用close 关闭文件描述符为 fd 的文件。 memcpy 函数简介: #include 函数原型: void *memcpy(void *dest, const void *src, size_t n); 功能:将src 中的内容复制到 dest 中,复制长度为 n。 参数简介: dest:为目标地址;src:为源地址; n:为字符串长度。 返回值:返回被赋值后的dest字符串指针。五,相关程序示例:
收藏 下载该资源
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号