免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1003 | 回复: 0
打印 上一主题 下一主题

linux内核分析(1) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-01-08 11:17 |只看该作者 |倒序浏览

                Memory Management (内存管理)

    内存管理子系统是操作系统的重要部分。从计算机发展早期开始,就存在对于大于系统中物理能力的内存需要。为了克服这种限制,开发了许多种策略,其中最成功的就是虚拟内存。虚拟内存通过在竞争进程之间共享内存的方式使系统显得拥有比实际更多的内存。
    虚拟内存不仅仅让你的计算机内存显得更多,内存管理子系统还提供:
    Large Address Spaces
(巨大的地址空间)操作系统使系统显得拥有比实际更大量的内存。虚拟内存可以比系统中的物理内存大许多倍。
   
   
   
    Shared Virtual Memory
(共享虚拟内存)虽然虚拟内存允许进程拥有分离(虚拟)的地址空间,有时你也需要进程之间共享内存。例如,系统中可能有多个进程运行命令解释程序
3.1 An Abstract Model of Virtual Memory
(虚拟内存的抽象模型)

    在考虑 Linux
支持虚拟内存的方法之前,最好先考虑一个抽象的模型,以免被太多的细节搞乱。
    在进程执行程序的时候,它从内存中读取指令并进行解码。解码指令也许需要读取或者存储内存特定位置的内容,然后进程执行指令并转移到程序中的下一条指令。进程不管是读取指令还是存取数据都要访问内存。
    在一个虚拟内存系统中,所有的地址都是虚拟地址而非物理地址。处理器通过操作系统保存的一组信息将虚拟地址转换为物理地址。
    为了让这种转换更简单,将虚拟内存和物理内存分为适当大小的块,叫做页(
    图 3.1 显示了两个进程的虚拟地址空间,进程 X 和进程 Y
,每一个进程拥有自己的页表。这些页表将每一个进程的虚拟页映射到内存的物理页上。图中显示进程
    有效标志 表示页表本条目是否有效
    本页表条目描述的物理页编号
    访问控制信息 描述本页如何使用:是否可以写?是否包括执行代码?
    页表通过虚拟页标号作为偏移来访问。虚拟页编号 5 是表中的第 6 个元素( 0
是第一个元素)
    要将虚拟地址转换到物理地址,处理器首先找出虚拟地址的页编号和页内偏移量。使用


    处理器使用虚拟页编号作为索引在进程的页表中找到它的页表的条目。如果该条目有效,处理器从该条目取出物理的页编号。如果本条目无效,就是进程访问了它的虚拟内存中不存在的区域。在这种情况下,处理器无法解释地址,必须将控制权传递给操作系统来处理。
    处理器具体如何通知操作系统进程在访问无法转换的无效的虚拟地址,这个方式是和处理器相关的。处理器将这种信息(
    假设这是一个有效的页表条目,处理器取出物理页号并乘以页大小,得到了物理内存中本页的基础地址。最后,处理器加上它需要的指令或数据的偏移量。
    再用上述例子,进程 Y 的虚拟页编号 1 映射到了物理页编号 4 (起始于
0x8000 , 4x 0x2000 ),加上偏移 0x194 ,得到了最终的物理地址 0x8194 。
    通过这种方式将虚拟地址映射到物理地址,虚拟内存可以用任意顺序映射到系统的物理内存中。例如,图
3.1.1 Demand Paging
    因为物理内存比虚拟内存少得多,操作系统必须避免无效率地使用物理内存。节省物理内存的一种方法是只加载执行程序正在使用的虚拟页。例如:一个数据库程序可能正在数据库上运行一个查询。在这种情况下,并非所有的数据必须放到内存中,而只需要正被检查的数据记录。如果这是个查找型的查询,那么加载程序中增加记录的代码就没什么意义。这种进行访问时才加载虚拟页的技术叫做
    当一个进程试图访问当前不在内存中的虚拟地址的时候处理器无法找到引用的虚拟页对应的页表条目。例如:图
    如果出错的虚拟地址无效意味着进程试图访问它不应该访问的虚拟地址。也许是程序出错,例如向内存中任意地址写。这种情况下,操作系统会中断它,从而保护系统中其他的进程。
    如果出错的虚拟地址有效但是它所在的页当前不在内存中,操作系统必须从磁盘映像中将相应的页加载到内存中。相对来讲磁盘存取需要较长时间,所以进程必须等待直到该页被取到内存中。如果当前有其他系统可以运行,操作系统将选择其中一个运行。取到的页被写到一个空闲的页面,并将一个有效的虚拟页条目加到进程的页表中。然后这个进程重新运行发生内存错误的地方的机器指令。这一次虚拟内存存取进行时,处理器能够将虚拟地址转换到物理地址,所以进程得以继续运行。
    Linux 使用
3.1.2 Swapping (交换)
    如果进程需要将虚拟页放到物理内存中而此时已经没有空闲的物理页,操作系统必须废弃物理空间中的另一页,为该页让出空间。
    如果物理内存中需要废弃的页来自磁盘上的映像或者数据文件,而且没有被写过所以不需要存储,则该页被废弃。如果进程又需要该页,它可以从映像或数据文件中再次加载到内存中。
    但是,如果该页已经被改变,操作系统必须保留它的内容以便以后进行访问。这种也叫做
    如果决定哪些页需要废弃或者交换的算法效率不高,则会发生颠簸(
    Linux 使用
3.1.3 Shared Vitual Memory (共享虚拟内存)

    虚拟内存使多个进程可以方便地共享内存。所有的内存访问都是通过页表,每一个进程都有自己的页表。对于两个共享一个物理内存页的进程,这个物理页编号必须出现在两个进程的页表中。
    图 3.1 显示了两个共享物理页号 4 的进程。对于进程 X 虚拟页号是 4
,而对于进程 Y 虚拟页号是 6 。这也表明了共享页的一个有趣的地方:共享的物理页不必存在共享它的进程的虚拟内存空间的同一个地方。
3.1.4 Physical and Vitual Addressing Modes
(物理和虚拟寻址模式)

    对于操作系统本身而言,运行在虚拟内存中没有什么意义。如果操作系统必须维护自身的页表,这将会是一场噩梦。大多数多用途的处理器同时支持物理地址模式和虚拟地址模式。物理寻址模式不需要页表,处理器在这种模式下不需要进行任何地址转换。
    Alpha AXP
处理器没有特殊的物理寻址模式。它将内存空间分为几个区,将其中两个指定为物理映射地址区。核心的地址空间叫做


3.1.5 Access Control (访问控制)  
    页表条目也包括访问控制信息。当处理器使用页表条目将进程的虚拟地址映射到物理地址的时候,它很容易利用访问控制信息控制进程不要用不允许的方式进行访问。
    有很多原因你希望限制对于内存区域的访问。一些内存,比如包含执行代码,本质上是只读的代码,操作系统应该禁止进程写它的执行代码。反过来,包括数据的页可以写,但是如果试图执行这段内存应该失败。大多数处理器有两种执行状态:核心态和用户态。你不希望用户直接执行核心态的代码或者存取核心数据结构,除非处理器运行在核心态。
    访问控制信息放在 PTE ( page table entry
)中,而且和具体处理器相关。图 3.2 显示了 Alpha AXP 的 PTE 。各个位意义如下:
V 有效,这个
FOE “
FOW “ Fault on Write” 如上,在试图写本页时产生 page fault
FOR “
ASM 地址空间匹配。用于操作系统清除转换缓冲区中的部分条目
KRE 核心态的代码可以读本页
URE 用户态的代码可以读本页
GII 间隔因子,用于将一整块映射到一个转换缓冲条目而非多个。
KWE 核心态的代码可以写本页
UWE 用户态的代码可以写本页
Page frame number 对于 V 位有效的 PTE ,包括了本 PTE
的物理页编号;对于无效的 PTE ,如果不是 0 ,包括了本页是否在交换文件的信息。
以下两位由 Linux 定义并使用
_PAGE_DIRTY 如果设置,本页需要写到交换文件中。
_PAGE_ACCESSED Linux 使用,标志一页已经访问过
3.2 Caches (高速缓存)
    如果你用以上理论模型来实现一个系统,它可以工作,但是不会太高效率。操作系统和处理器的设计师都尽力让系统性能更高。除了使用更快的处理器、内存等,最好的方法是维护有用信息和数据的高速缓存,这会使一些操作更快。
    Buffer Cache : Buffer cache
包含了用于块设备驱动程序的数据缓冲区。这些缓冲区大小固定(例如 512
字节),包括从块设备读出的数据或者要写到块设备的数据。块设备是只能通过读写固定大小的数据块来访问的设备。所有的硬盘都是块设备。块设备用设备标识符和要访问的数据块编号作为索引,用来快速定位数据块。块设备只能通过
参见 fs/buffer.c
    Page Cache
用来加快对磁盘上映像和数据的访问。它用于缓存文件的逻辑内容,一次一页,并通过文件和文件内的偏移来访问。当数据页从磁盘读到内存中时,被缓存到
参见 mm/filemap.c
    Swap Cache 只有改动过的(或脏 dirty
)页才存在交换文件中。只要它们写到交换文件之后没有再次修改,下一次这些页需要交换出来的时候,就不需要再写到交换文件中,因为该页已经在交换文件中了,直接废弃该页就可以了。在一个交换比较厉害的系统,这会节省许多不必要和高代价的磁盘操作。
参见 mm/swap_state.c mm/swapfile.c


Hardware Cache: 硬件高速缓存的常见的实现方法是在处理器里面: PTE
的高速缓存。这种情况下,处理器不需要总是直接读页表,而在需要时把页转换表放在缓存区里。
    当引用虚拟地址时,处理区试图在 TLB
中寻找。如果找到了,它就直接将虚拟地址转换到物理地址,进而对数据执行正确的操作。如果找不到,它就需要操作系统的帮助。它用信号通知操作系统,发生了
    高速缓存的副作用(不管是硬件或其他方式的)在于 Linux
必须花大量时间和空间来维护这些高速缓存区,如果这些高速缓存区崩溃,系统也会崩溃。
3.3 Linux Page Tables ( Linux 页表)  
    Linux 假定了三级页表。访问的每一个页表包括了下一级页表的页编号。图 3.3
显示了一个虚拟地址如何分为一系列字段:每一个字段提供了在一个页表中的偏移量。为了将虚拟地址转换为物理地址,处理器必须取得每一级字段的内容,转换为包括该页表的物理页内的偏移,然后读取下一级页表的页编号。重复三次直到包括虚拟地址的物理地址的页编号找到为止。然后用虚拟地址中的最后一个字段:字节偏移量,在页内查找数据。
    Linux
运行的每一个平台都必须提供转换宏,让核心处理特定进程的页表。这样,核心不需要知道页表条目的具体结构或者如何组织。通过这种方式,
参见 include/asm/pgtable.h
3.4 Page Allocation and Deallocation (
页的分配和回收 )

    系统中对于物理页有大量的需求。例如,当程序映像加载到内存中的时候,操作系统需要分配页。当程序结束执行并卸载时需要释放这些页。另外为了存放核心相关的数据结构比如页表自身,也需要物理页。这种用于分配和回收页的机制和数据结构对于维护虚拟内存子系统的效率也许是最重要的。
    系统中的所有的物理页都使用 mem_map 数据结构来描述。这是一个
mem_map_t 结构的链表,在启动时进行初始化。每一个 mem_map_t (容易混淆的是这个结构也被称为 page
结构)结构描述系统中的一个物理页。重要的字段(至少对于内存管理而言)是:
参见 include/linux/mm.h
count 本页用户数目。如果本页由多个进程共享,计数器大于 1 。
Age 描述本页的年龄。用于决定本页是否可以废弃或交换出去。
Map_nr mem_map_t 描述的物理页编号。
    页分配代码使用 free_area
向量来查找空闲的页。整个缓冲管理方案用这种机制来支持。只要用了这种代码,处理器使用的页的大小和物理页的机制就可以无关。
    每一个 free_area
单元包括页块的信息。数组中的第一个单元描述了单页,下一个是 2 页大小的块,下一个是 4 页大小的块,以此类推,依次向上都是 2
的倍数。这个链表单元用作队列的开头,有指向 mem_map 数组中页的数据结构的指针。空闲的页块在这里排队。 Map
是一个跟踪这么大小的页的分配组的位图。如果页块中的第
    图 3.4 显示了 free_area 结构。单元 0 有一个空闲页(页编号 0
),单元 2 有 2 个 4 页的空闲块,第一个起始于页编号 4 ,第二个起始于页编号 56 。
3.4.1 Page Allocation ( 页分配
)

参见 mm/page_alloc.c get_free_pages()
    Linux 使用  
min_free_pages ),分配代码就会在
    这个算法首先找它请求大小的内存页块。它跟踪 free_area 数据结构中的
list 单元队列中的空闲页的链表。如果请求大小的页块没有空闲,就找下一个尺寸的块(


    例如在图 3.4 中,如果请求 2 页的数据块,第一个 4 页块(起始于页编号 4
)将会被分为两个 2 页块。起始于页号 4 的第一个 2 页块将会被返回给调用者,而第二个 2 页块(起始于页号 6 )将会排在 free_area
数组中的单元 1 中 2 页空闲块的队列中。
3.4.2 Page Deallocation (页回收)

    分配页块的过程中将大的页块分为小的页块,将会使内存更为零散。页回收的代码只要可能就把页联成大的页块。其实页块的大小很重要(
    只要一个页块回收,就检查它的相邻或一起的同样大小的页块是否空闲。如果是这样,就把它和新释放的页块一起组成以一个新的下一个大小的空闲页块。每一次两个内存页块组合成为更大的页块时,页回收代码都要试图将页块合并成为更大的块。这样,空闲的页块就会尽可能的大。
    例如,在图 3.4 ,如果页号 1 释放,那么它会和已经空闲的页号 0
一起组合并放在 free_area 的单元 1 中空闲的 2 页块队列中。
3.5 Memory Mapping (内存映射)
    当一个映像执行时,执行映像的内容必须放在进程的虚拟地址空间中。对于执行映像连接到的任意共享库,情况也是一样。执行文件实际并没有放到物理内存,而只是被连接到进程的虚拟内存。这样,只要运行程序引用了映像的部分,这部分映像就从执行文件中加载到内存中。这种映像和进程虚拟地址空间的连接叫做内存映射。
    每一个进程的虚拟内存用一个 mm_struct
数据结构表示。这包括当前执行的映像的信息(例如 bash )和指向一组 vm_area_struct 结构的指针。每一个
    当一个执行映像映射到进程的虚拟地址空间时,产生一组
3.6 Demand Paging
    只要执行映像映射到进程的虚拟内存中,它就可以开始运行。因为只有映像的最开始的部分是放在物理内存中,很快就会访问到还没有放在物理内存的虚拟空间区。当进程访问没有有效页表条目的虚拟地址的时候,处理器向
    Linux 必须找到
参见 handle_mm_fault() in mm/memory.c
    Linux 然后检查
    现在 Linux 确定 page fault 是合法的,它必须进行处理。
Linux 必须区分在交换文件和磁盘映像中的页,它用发生 page fault 的虚拟地址的页表条目来确定。
参见 do_no_page() in mm/memory.c
    如果该页的页表条目是无效的但非空,此页是在交换文件中。对于 Alpha AXP
页表条目来讲,有效位置位但是
    并非所有的 vm_area_struct
数据结构都有一整套虚拟内存操作,而且那些有特殊的内存操作的也可能没有
    通常的 Linux nopage 操作用于对执行映像的内存映射,并使用 page
cache
将请求的映像页加载到物理内存中。虽然在请求的页调入的物理内存中以后,进程的页表得到更新,但是也许需要必要的硬件动作来更新这些条目,特别是如果处理器使用了
参见 mm/filemap.c 中 filemap_nopage()


 


3.7 The Linux Page Cache
    Linux 的
参见 linux/pagemap.h
    当一页的数据从内存映射文件中读出,例如当 demand paging
时需要放到内存中的时候,此页通过
    当程序映像读取和执行的时候 page cache
不断增长。如果页不在需要,将从缓存中删除。比如不再被任何进程使用的映像。当
3.8 Swapping out and Discarding Pages
(交换出去和废弃页)

    当物理内存缺乏的时候, Linux
内存管理子系统必须试图释放物理页。这个任务落在核心交换进程上(
    核心交换守护进程( kswapd )在启动时由核心的 init
进程启动,并等待核心的交换计时器到期。每一次计时器到期,交换进程检查系统中的空闲页数是否太少。它使用两个变量:
参见 mm/vmscan.c 中的 kswapd()
减少 buffer cache 和 page cache 的大小
将系统 V 的共享内存页交换出去
交换和废弃页
    如果系统中的空闲页数低于 free_pages_low
,核心交换进程将试图在下一次运行前释放 6 页。否则试图释放 3
页。以上的每一种方法都要被尝试直到释放了足够的页。核心交换进程记录了它上一次使用的释放物理页的方法。每一次运行时它都会首先尝试上一次成功的方法来释放页。
    释放了足够的页之后,交换进程又一次睡眠,直到它的计时器又一次过期。如果核心交换进程释放页的原因是系统空闲页的数量少于
3.8.1 Reducing the size of the Page and
Buffer Caches

    page 和
    每一次当核心交换进程要缩小这些缓存区时,它要检查 mem_map
页矢量中的页块,看是否可以从物理内存中废弃。如果系统空闲页太低(比较危险时)而核心交换进程交换比较厉害,这个检查的页块大小就会更大一些。页块的大小进行循环检查:每一次试图减少内存映射时都用一个不同的页块大小。这叫做
参见 mm/filemap.c shrink_map()
    检查的每一页都要判断缓存在 page cache 或者 buffer cache
中。注意共享页的废弃这时不考虑,一页不会同时在两个缓存中。如果该页不在这两个缓冲区中,则
    缓存在 buffer cache ch
中的页(或者说页中的缓冲区被缓存)使缓冲区的分配和释放更有效。缩小内存映射的代码试图释放包含检查过的页的缓冲区。如果缓冲区释放了,则包含缓冲区的页也被释放了。如果检查的页是在
参见 fs/buffer.c free_buffer()
    如果这次尝试释放了足够的页,核心交换进程就会继续等待直到下一次被周期性地唤醒。因为释放的页不属于任何进程的虚拟内存(只是缓存的页),因此不需要更新进程的页表。如果废弃的缓存页仍然不够,交换进程会试图交换出一些共享页。
3.8.2 Swapping Out System V Shared Memory
Pages (交换出系统 V 的共享内存页)

    系统 V
的共享内存是一种进程间通讯的机制,通过两个或多个进程共享虚拟内存交换信息。进程间如何共享内存在第
    核心交换进程将系统 V 的共享内存页交换出去时也用 clock
算法。它每一次运行都记录了上一次交换出去了那一块共享内存的那一页。它用两个索引来记录:第一个是
参见 ipc/shm.c shm_swap()
    因为一个指定的系统 V
共享内存的虚拟页对应的物理页号包含在每一个共享这块虚拟内存的进程的页表中,所以核心交换进程必须修改所有的进程的页表来体现此页已经不在内存而在交换文件中。对于每一个交换出去的共享页,交换进程必须找到在每一个共享进程的页表中对应的此页的条目(通过查找每一个
    如果所有共享的内存都修改过,页的在用数变为 0
,这个共享页就可以写到交换文件中。这个系统 V 共享内存区的 shmid_ds
数据结构指向的页表中此页的条目将会换成交换出的页表条目。交换出的页表条目无效但是包含一个指向打开的交换文件的索引和此页在此文件内的偏移量。这个信息用于将此页再取回物理内存中。
3.3 Swapping Out and Discarding Pages

    交换进程轮流检查系统中的每一个进程是否可以用于交换。好的候选是可以交换的进程(有一些不行)并且有可以从内存中交换出去或废弃的一个或多个页。只有其他方法都不行的时候才会把页从物理内存交换到系统交换文件中。
参见 mm/vmscan.c swap_out()
    来自于映像文件的执行映像的大部分内容可以从文件中重新读出来。例如:一个映像的执行指令不会被自身改变,所以不需要写到交换文件中。这些页只是被简单地废弃。如果再次被进程引用,可以从执行映像再次加载到内存中。
    一旦要交换的进程确定下来,交换进程就查看它的所有虚拟内存区域,寻找没有共享或锁定的区域。
    参见 mm/vmscan.c swap_out_vme() 跟踪进程
mm_struct 中排列的 vm_area_struct 结构中的 vm_next vm_nex 指针。
    Linux 的交换算法使用了页的年龄。每一个页都有一个计数器(放在
mem_map_t 数据结构中),告诉核心交换进程此页是否值得交换出去。页不用时变老,访问时更新。交换进程只交换老的页。缺省地,页第一次分配时年龄赋值为
    如果页太老 ( 年龄 age = 0) ,交换进程会进一步处理。脏页可以交换出去,
Linux 在描述此页的 PTE 中用一个和体系结构相关的位来描述这种页(见图 3.2
)。但是,并非所有的脏页都需要写到交换文件。每一个进程的虚拟内存区域都可以拥有自己的交换操作(由
    此页的页表条目会用一个无效的条目替换,但是包括了此页在交换文件的信息:此页所在文件内的偏移和所用的交换文件。不管什么方式交换,原来的物理页被放回到
    如果交换或废弃了足够的可交换进程的页,交换进程重新睡眠。下一次唤醒时它会考虑系统中的下一个进程。这样,交换进程轻咬去每一个进程的物理页,直到系统重新达到平衡。这种做法比交换出整个进程更公平。
3.9 The Swap Cache (交换缓存)
    当把页交换到交换文件时, Linux
会避免写不必要写的页。有时可能一个页同时存在于交换文件和物理内存中。这发生于一页被交换出内存然后在进程要访问时又被调入内存的情况下。只要内存中的页没有被写过,交换文件中的拷贝就继续有效。
    Linux 用
    当 Linux
需要交换一个物理页到交换文件的时候,它查看交换缓存,如果有此页的有效条目,它不需要把此页写到交换文件。因为内存中的此页从上次读到交换文件之后没有被修改过。
    交换缓存中的条目是曾经交换出去的页表条目。它们被标记为无效,但是包含了允许
3.10 Swapping Page In (交换进)

    保存在交换文件中的脏页可能又需要访问。例如:当应用程序要向虚拟内存中写数据,而此页对应的物理页交换到了交换文件时。访问不在物理内存的虚拟内存页会引发
参见 arch/i386/mm/fault.c do_page_fault()
    执行了合适的和处理器相关的动作并找到了包括错误(发生)的虚拟地址的有效的虚拟内存,
参见 mm/memory.c do_no_page()
    这时, Linux 知道了错误(发生)的虚拟地址和关于此页交换到哪里去的页表条目。
参见 mm/page_alloc.c swap_in()
    如果引起 page fault
的访问不是写访问,页就留在交换缓存中,它的页表条目标记为不可写。如果后来此页又被写,会产生另一个
    如果将此页从交换文件调回的访问是写访问,这个页就从交换缓存中删除,此页的页表条目页标记为脏页和可写。
               
               
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/29191/showart_228160.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP