[Linux驱动入门]内存管理

来源:互联网 发布:淘宝比较出名的零食店 编辑:程序博客网 时间:2024/06/05 02:43

第六章 内存管理

6.1 内存管理中的基本概念

1. 页

 内核把物理页作为内存管理的基本单位。大多数32位体系结构的处理器支持4KB的页,内核用struct page结构表示系统的物理页,这个page与物理页无关,而并非与虚拟页相关。页的拥有者可能是用户空间进程、动态分配的内核数据、静态内核代码或页高速缓存等。

2. 区

  由于有些页位于内存特定的物理地址上,所以不能将其用于一些特定的任务,故内核把页分成不同的区,分别是ZONE_DMA,ZONE_DMA32,ZONE_NORMAL,ZONE_HIGHMEM.

 

6.2 申请内存的几种方法

  alloc_page + page_address = _get_free_pages:需要以页为单位的一族连续物理页时,尤其只需要一两页。

  kmalloc:连续的物理页。传递给这个函数的常用标志GFP_KERNELGPF_ATOMIC.

  vmalloc:不需要连续的物理地址,只要连续的虚拟地址。

  alloc_pages:需要从高端内存进行分配(高端内存物理地址高于896MB中的页被映射到3~4GB上)

   如果需要创建和撤销很多大的数据结构,就需要考虑建立slab缓存,能极大地提高对象分配和回收的性能。

   slab分配器的使用:

struct kmem_cache * task_struct_cachep;

task_struct_cachep = kmem_cache_create(task_struct,sizeof(task_struct),ARCH_MIN_TASKALIGN,SLAB_PANIC|SLAB_NOTRACK,NULL);     //创建高速缓存

struct  task_struct * tsk;

tsk = kmem_cache_alloc(task_struct_cachep,GPF_KERNEL);     //从缓存中分配

……

kmem_cache_free(task_struct_cachep,tsk);          //从缓存中释放

kmem_cache_destory(task_struct_cachep);         //撤销高速缓存

6.3 内核栈

  每个进程都有两个页的内核栈,32位和64位体系结构处理器的页面大小分别是4KB和8KB,所以内核栈的大小分别为8KB和16KB。由于连续两页有时难以寻找到,所以引入单页内核栈。当使用一个页面的内核栈时,中断处理程序就不放在栈里,为每一个进程提供一个用于中断处理程序的栈,即中断栈,这样中断处理程序不用再和被中断进程共享一个内核栈。

中断处理程序和被中断的进程共享一个栈,不过当1页栈被激活,中断处理程序就获得自己的栈。

 

6.4 进程地址空间的基本概念

  进程地址空间:用户空间中的进程的内存

  内核使用内存描述符结构体表示进程的地址空间,内存描述符由struct mm_struct结构体表示。分配内存描述符有两种方法:其一:fork函数利用copy_mm函数复制父进程的内存描述符,也就是current->mm域给子进程,而子进程中的mm_struct结构体实际上是通过allocate_mm宏从mm_cachep_slab缓存中分配得到的;其二,当CLONE_VM被指定后,内核就不需要调用allocate_mm函数,而仅仅需要在调用copy_mm函数中将mm域指向其父进程的内存描述符。

  1.内核线程与内存描述符

   内核线程没有进程地址空间,也就没有自己的内存描述符,内核线程是没有用户上下文的。为了避免内核线程为内核描述符分配页表浪费内存,内核线程直接使用前一个进程的内存描述符,而且仅仅使用前一个进程的描述符中和内核内存相关的信息,这些信息的含义和普通进程完全相同。

 

  2.内存区域的树形结构和内存区域的链表结构

   红黑树特点:

  1)左边节点值小于右边节点值;

  2)红节点的子节点为黑色;

  3)树中的任何一条从节点到叶子的路径必须包括相同数量的黑色节点;

  4)根节点为红色;

  5)红黑色搜索、插入、删除等操作的复杂度都是Olog(n);

   链表用于需要遍历全部节点的时候,而红黑树适合在地址空间中定位特定内存区域的时候,内核为了内存区域上的不同的操作获得更高的性能,所以同时使用红黑树和链表这两种数据结构。

 

6.5 创建和撤销内存区域

   如果创建内核区域,系统会调用do_mmap函数,在用户空间可以通过mmap函数获取内核函数do_mmap函数的功能。如果需要撤销内核区域,系统会调用do_mmap函数,在用户空间可以调用munmap函数获取内核do_munmap函数的功能。

   原则上用户空间不能访问设备空间,mmap函数能把用户空间和设备空间进行映射,使得对用户空间的访问等同于对设备空间的访问。mmap具体功能有两个:其一,将普通文件映射到内存中,通常在需要对文件进行频繁读写时使用,这样用内存读写取代I/O读写,以获得较高的性能。其二,为无关联的进程提供共享内存空间,一般也是将一个普通文件映射到内存中。

   用户调用mmap函数,内核会进行如下处理:

   1)在进程的虚拟空间查找一块VMA

   2)将这块VMA进行映射;

   3)如果设备驱动程序或者文件系统的file_operations定义了mmap操作,

        则调用它。

   4)将这个VMA插入进程的VMA链表中。

 

6.6 页表

当应用程序访问一个虚拟地址时,首先必须将虚拟地址转换为物理地址,然后处理器才能解析地址访问请求。地址转换需要将虚拟地址分段,使每段虚拟地址都作为一个索引指向页表。

搜索内存中物理地址速度有限,为了加快搜索,多数体系结构都实现了一个TLB(转换旁路缓存),TLB作为一个将虚拟地址映射到物理地址的硬件缓存。

 

6.7 页高速缓存与页回写

  页高速缓存(cache)是Linux内核实现磁盘缓存,通过把磁盘中的数据缓存到物理内存中,把对磁盘的访问变为对物理内存的访问。页回写是将页高速缓存中的变更数据刷新回磁盘的操作。引入磁盘高速缓存的目的主要有两个:

(1)访问内存速度比访问磁盘快得多;

(2)临时局部原理;

  

  读后备存储:首先检查需要的数据是否在页高速缓存中,如果在,则放弃访问磁盘,而直接从内存中读取,这个行为叫缓存命中。如果缓存未命中,那么内核必须调度块I/O操作,从磁盘读取数据,然后内核将读来的数据放入页缓存中,这样方便后期的缓存命中。

  

   写缓存的方法如下:

(1)不缓存,即直接将数据写到磁盘,不通过页高速缓存;

(2)写透缓存,即写操作自动更新内存缓存,同时也更新磁盘文件;

(3)回写,即后备存储不立即直接更新,由一个回写进程周期性将脏页链表中的页写回磁盘,这样通过延迟写磁盘,可以方便在以后的时间内合并更多的数据和再一次刷新。

  

  缓存回收:即缓存中的什么内容将被清除的策略。

 

  基树:页高速缓存通过两个参数address_space对象上加一个偏移量进行搜索,每个address_space对象都有唯一的基树,它保存在address_space中的page_tree结构体中。

 

  缓存区高速缓存:独立磁盘块通过I/O缓冲也要被存入页高速缓存,这个缓存叫做缓存区高速缓冲。

 

  脏页被回写放生在如下三个条件:

(1)当空闲内存低于一个特定阈值时;

(2)当脏页在内存中驻留时间超过一个特定的阈值时;

(3)当用户进程调用syns()fsync()系统调用时,内核会按要求执行回写操作。

0 0
原创粉丝点击