Linux 虚存 linux2.6内核特性

1、大型页面的支持node

    当代计算机体系结构大都支持多种页面大小,例如,IA-32体系结构支持4KB4MB的页面, Linux操做系统只是将大型页面用于映射实际的内核映像。大型页面的使用主要是为了改进高性能计算(HPC)以及其余内存密集型应用的性能。任何占用大量虚存的访存密集型应用程序均可以使用大型页面来改进性能(Linux使用 2MB4MB的大型页面, AIX使用 16MB的大型页面,而 Solaris使用的页面大小为 4MB)大型页面可以提升性能是由于转换查找缓冲区(Translation Lookaside bufferTLB)可以映射到更大的虚存空间(经过增长 TLB的可达范围),从而减小了 TLB不命中次数。大型页面经过消除在4KB边界上重启预取操做的需求,还改进了内存预取过程 。
    在Linux 2.6内核中,实际可用的大型页面数量可经过proc(/proc/sys/vm/nr_hugepages)接口来配置。因为大页面的实际分配状况与系统中是否存在着物理连续的内存区域有关,建议在系统启动时分配大型页面。 Linux 2.6内核中大型页面的实现核心称为 hugetlbfs,是一种基于 ramfs的伪文件系统(fs/hugetlbfs/inode.c中实现)。该实现的思想使用用大页面来备份存在于文件系统中的任何文件。实际的伪文件系统在系统启动时初始化并注册为一种内部文件系统。进程访问大型页面的方式有两种:经过 shmget()接口创建一个由大型页面 back的共享区域,或者经过对一个已在大型页面文件系统中打开的文件使用mmap()调用。要对一个使用了大型页面的文件进行操做的话, 首先应挂接一个 hugetlbfs文件系统。完成该挂接操做以后,就可使用标准的文件操做函数调用如 open()等来设置一个文件描述符。 在一个已打开的文件上使用 mmap()系统调用时, hugetlbfs文件系统注册的 mmap()函数为该进程建立适当的 vma结构。推荐使用/proc/meminfo接口以确保可以按预期设置大型页面的容量及数量。分片操做可能会致使该分配过程没法成功地完成 。

web

2、页面分配与替换算法

    Linux 2.6内核的物理页面分配机制仍基于伙伴(buddy)算法,但该机制自己已被扩展, 从而相似于一种将合并过程加以延迟处理的懒伙伴(lazy buddy)机制。合并过程当中的缓冲区释放活动包含两个步骤:首先将一个缓冲区推入空闲列表中,使得该缓冲区可用于其余请求;而后将该缓冲区的引用状态标记为空闲(一般在一个位图之中),并将该缓冲区与相邻的缓冲区加以合并(若是可行的话)。一般的 buddy系统实如今每次释放时都会执行这两个步骤。而 lazy buddy机制执行第一个步骤后延迟执行第二个步骤,是基于缓冲区的状态来执行该步骤的。下面所讨论的懒伙伴概念在 Linux 2.6中的实现采用了基于 CPU的页面列表。 这些页面集合包含了两个页面(热页面和冷页面)列表, 其中热页面是最近已被使用过的页面,所以指望这些页面仍存在于 CPUcache中。在分配时, 首先查询当前 CPU的页面集合以肯定这些页面是否可用。若是可用, 则分配过程相应地继续进行。要肯定应该什么时候释放或分配该页面集合, 则使用一种基于高低水印(low andhigh watermark)的方法。当到达低水印值时,会分配一批页面并将其置于该表中;当到达高水印值时,会同时释放一批页面。这种新型改进实现的结果是极大下降了自旋锁的开销。

数组

3、 Slab分配器缓存

    和其余大多数 UNIX操做系统同样, Linux也提供了一个通用的内存 slab分配器,用来完成任意空间大小的内存分配。 全部 slab分配器的基本目标都是高效地分配一般小于一个页面或者不是页面尺寸偶数倍的小型内存缓冲区。其另外一个目标是对频繁分配和释放的内存对象加以操做,这个过程可能致使出现内存分片现象。 slab分配器容许对这些经常使用的对象(例如 sockinodemm_struct)加以缓存,从而尽可能减小每次请求一个新对象时初始化和破坏这些基本实体所需的开销。 数据结构


4、VM的可调参数
    全部这些参数均可以经过 proc(/proc/sys/vm)文件系统接口进行调优。下面按照与proc文件系统中相同的字母顺序列出这些参数。
  • dirty_background_ratio 参数是在尝试一个回写(writeback)操做以前所持有的脏内存比例。在 Linux 2.6内核中, pdflush内核线程池负责 VM的回写操做, 并周期性同步文件系统的元数据。若超出了这个后台回写百分比,则 pdflush守护进程异步处理回写操做。
  • dirty_expire_centisecs 参数是数据能够保持为脏状态的最大厘秒数, 这个时间段经过查询全部在内存中缓存了脏页面的文件的时间戳来肯定。该参数表示一个上限值,确保最终能够将数据写到磁盘子系统。厘秒表示1/100s的时间单位。
  • dirty_ratio 参数是过量脏内存的比例。当一个任务在脏内存过多的环境中执行文件写操做时,系统必需要对脏内存页面执行写出操做,直至脏内存页面的百分比低于系统规定的阈值。这一点很重要, 由于 I/O操做被看做具备高延迟。 若是某个任务在系统可以知足一个分配请求以前须要等待回写操做完成,则该任务的性能会受到影响。这主要被看做是大型内存系统中的一个问题。
  • dirty_writeback_sentisecs 参数是 pdflush内核线程执行回写操做之间的时间间隔(以厘秒为单位)。前面讨论过的 dirty_expire_centisecs 参数控制脏内存页面的存活时间,而该参数定义了 pdflush 线程的调用频率。建议该参数值小于dirty_expire_centisecs取值(对于多数工做负荷来讲, 16的比率应是合理的)。增长这个时间长度以及频率间隔(但仍遵循 16比率)可能有利于拥有内核密集型工做负荷的大型系统,由于后台回写操做可能会干扰到工做负荷自身的性能行为。
  • lower_zone_protection 参数是为内存区域回退操做的阻止因子分配的权重。Linux操做系统将物理内存分红 3个不一样区域。ZONE_DMA是可由(老式)I/O设备寻址的区域, ZONE_ NORMAL是可由内核寻址的区域, 而 ZONE_HIGHMEM区域则提供给用户空间和特定的内核(临时)缓冲区。上述概念的基本原理是ZONE_DMA区域可用做与 ZONE_NORMAL相同的用途,而 ZONE_NORMAL区域可用做与 ZONE_HIGHMEM 相同的用途,但反之则否则。当ZONE_HIGHMEM区域饱和时, 可使用 ZONE_NORMAL区域进行分配(经过一个回退操做)ZONE_NORMAL是惟一可用于保存内核数据结构的区域,对于系统而言是相当重要的。在Linux中,使用一种称为递增最小量(incremental min)的启发算法来阻止回退至重要内存区域(至少 ZONE_NORMAL)。lower_zone_protection置为默认的零值会禁止递增最小量算法,将该参数值设置为 4会使其效果增至4倍。这意味着该参数当前控制较低区域防御算法的强度。对于可能占用系统中较高区域的分配操做,lower_zone_protection这个可调参数能够提升对较低内存区域的保护程度 。
  • min_free_kbytes 参数规定了可用于诸如中断处理程序发起的紧急分配等的内存池的大小。在更大规模的系统上, 该参数会影响可用于中断时间的内存与可用于运行时分配的内存比率。在更小的内存系统上, 该参数被保持为较小值以免分配失败。广泛共识是这个低水印参数用于肯定什么时候拒绝用户空间分配以及什么时候激发页面替换场景。有可能不将该值设置为任何较大的物理内存比例。若是用户空间操做是内存子系统主要消耗者,尤为适用。
  • nr_pdflush_thread 参数规定 pdflush(回写)线程池的当前大小。VM子系统利用一种动态算法来断定实际线程的数量。重点是在低内存的情形下(其中额外线程的动态分配可能会失败)保留特定数量的 pdflush线程。下限与上限(Linux中分别称为 MIN_PDFLUSH_THREADSMAX_PDFLUSH _THREADS)各自设置为 28。提升上限值(而且重编译内核)有助于在拥有大量磁盘驱动器以及绑定寻道操做的工做负荷的环境中提升 VM I/O并发性。提升最大值会改进性能的另外一种状况是与回写操做相关的 CPU绑定情形,这时系统中可提供额外处理器来处理整体工做负荷。
  • overcommit_memory 参数是一个容许内存过量使用的标志。当该标志置为默认值 0时,内核在每一个 malloc()调用前都发起检查以确保存在足够的内存。若该标志置为 1,则假定系统中老是存在着足够多的内存。当该标志置为 0时,则确保只会分配页面替换基础架构所能处理的用户虚存量,而且每一个页面都须要一个后备存储器。匿名内存页面须要交换空间,而文件页面则须要能够被保留的磁盘空间。将该标志置为1后会生成更有效的内存利用行为。可是,基于特定工做负荷,它可能致使某些进程被关闭的场景。概言之,将该标志置为 0会致使内核经过估测可用的内存量和明显无效的失败请求,执行启发式的内存过量使用处理操做。不幸的是,因为系统使用一种启发式而不是精确的算法来分配内存,这种设置有时会致使系统可用内存过载。将该标志置为 1会致使内核执行内存无过量使用方式的处理。这种设置会增大内存过载的可能性,但也会提升内存密集型任务(例如某些科学应用所执行的任务)的性能。另外一种支持情形是将该标志置为2。在这种状况下,对于所有交换空间以及 overcommit_ratio 参数(在下面讨论)中规定的物理内存百分比之和的内存请求,内核会分配失败。 在 Linux中, 将该标志置为2能够下降内存过量使用的风险。
  • overcommit_ratio 参数指定了当(前面讨论的)overcommit_memory参数置为2时所考虑的物理内存百分比。默认值是50
  • page-cluster 参数所表明的整数值当 2做为 x的乘幂时标识了内核一次性读入的页面数目(实际的交换预读窗口)。该参数的默认值对于内存小于 16MB的系统为 2,对于内存更大的系统为3, 这些取值在多数状况下均可看做是合理的设置。 该参数可用于改进页面 I/O效率, 但若是该参数被不当指定的话, 系统可能会面临过分的I/O和内存消耗。
  • swappiness 参数指示了 VM子系统在经过解除页面映射并将其换出的方式来回收页面与只回收未被任何进程映射的页面之间的相对优先选择。实际的决策依赖于一个二元开关,当映射至进程页表中的内存百分比的一半取值与 swappiness取值之和超过 100时激活该开关。 系统在决策过程当中进一步利用了某个危机因子,该因子在每次须要重试页面替换操做时都增长 2倍。若是系统难以定位可回收的未映射页面,则 VM子系统执行反向工做过程并开始对匿名内存进行页换出。若是不想对匿名内存进行页换出,则能够经过为 swappiness参数赋予较低取值来表达。若是 kswapd正在使用大量 CPU资源或者系统正在 iowait状态中耗费时间,则能够增长 swappiness参数值能够提升系统的总体性能。

5、CPU调度器
    线程调度器子系统负责将可用的 CPU资源分配到可运行的线程中。该调度器的行为及决策制定过程与线程的公平性和调度延迟直接相关。公平性可描述为全部线程不只都可以向前执行并且都以一种均衡的方式向前执行的能力。公平性的相反状况称为饿死,在这种情形下某个特定线程没法继续向前执行。公平性经常难以评判,由于这意味着全局和局部性能间的折衷。调度延迟描述了线程进入可运行状态(TSRUN)后以及在处理器上实际运行(TSONPROC)之间的实际延迟。低效的调度延迟行为会致使应用响应时间出现明显的迟滞。高效且有效的 CPU调度器对于计算平台的运行极为重要, 决定了每一个任务(线程)在什么时候开始运行以及运行的时间长度。 实时任务与分时任务是有区别的, 各自具备不一样的目标,并经过调度器中内含的不一样调度规则来实现。
    Linux为每一个任务分配一个静态优先级,能够经过 nice()接口对其进行修改。 Linux拥有一组优先级类型,用以区别实时任务和分时任务。优先级的取值越低, 任务的逻辑优先级或者换句话说其综合重要性(general importance)就越高。当阐述优先级提升或下降时,在这个上下文中的讨论老是指代逻辑优先级。实时任务的优先级老是高于分时任务。
    Linux 2.6内核的调度器称为 O(1)调度器,是一个多队列调度器,为每一个 CPU都分配一个实际的运行队列, 所以是一种 CPU本地调度方法。 O(1)这个标签描述了任务检索的时间复杂度,即不管系统上的工做负荷如何,均可以在常量时间内选定下一个运行任务。
    先前版本的
Linux调度器使用了优度(goodness)概念来肯定下一个待执行的线程。全部可运行的任务都保持在单个运行队列上,该队列是处于 TSRUN(TASK_RUNNABLE)状态中的线程所造成的一个链表。 Linux 2.6内核中将惟一的运行队列锁替换为基于每一个CPU的锁,从而确保在 SMP系统上具备更好的扩展性。该 O(1)调度器所采用的基于 CPU的运行队列机制将运行队列(按优先级顺序)分解成许多桶(bucket),并使用位图来标识拥有可运行任务的那些桶。若是要定位下一个待执行任务,须要读取该位图以标识出第一个具备可运行任务的桶并选择该桶的运行队列中的第一个任务。
    每一个
CPU的运行队列由两个任务列表向量组成, 称为活跃向量(active vector)和超时向量(expired vector)。每一个向量索引表明了拥有各自优先级的可运行任务的一个列表。一个任务在执行一段时间后,会从活跃列表移至超时列表,这样确保了全部可运行任务都可以得到执行机会。当该活跃数组为空时, 则经过修改指针来交换超时向量和活跃向量。
    系统有时须要执行一个负载平衡算法对各个运行队列进行平衡处理,以确保每一个
CPU上的任务数量都相近。前面提过,调度器须要决定即将运行哪些任务以及任务的运行时间长度。Linux内核中的时间量子定义为系统节拍的倍数。节拍定义为两个连续定时器中断之间的固定增量(1/HZ)Linux 2.6内核中, 参数 HZ被设置为 1 000, 这代表每毫秒激活一次中断例程 scheduler_tick(),此时当前运行的任务即执行了一个节拍。将参数 HZ被设置为 1 000并不会改进系统的响应效率,由于该设置不会影响到实际的调度器时间片。对于主要在数学运算(number-crunching)模式中执行的系统来讲,HZ=1 000的设置可能并不合适。在这种环境下,将参数 HZ设置为更低的取值(大约 100)有利于提升总体系统的性能。
    除了静态优先级
(static_prio)以外,每一个任务都还维护一个有效优先级(prio)其区别表示基于特定任务的最近平均休眠时间(sleep_avg)而获得的特定优先级奖励或处罚。平均休眠时间表示一个任务最近被取消调度的节拍数量。任务的有效优先级决定了其在运行队列的优先级列表中的位置。若是一个任务的有效优先级超出其静态优先级特定程度,则称该任务是交互式的。这种场景只能基于任务 accumulating平均休眠节拍数。Linux 2.6内核中嵌入的交互式估测器框架自动且透明地运行。在 Linux中,高优先级任务到达交互状态所需的平均休眠时间远小于低优先级任务。由于基于平均休眠时间来评估任务的交互性, I/O绑定的任务是到达交互状态的潜在候选,而 CPU绑定的任务一般不被当作是交互式的。实际的时间片被定义为一个任务在将 CPU(主动)转让给另外一个任务以前能够执行的最长时间,只是正常任务的静态优先级的线性函数。 优先级自己对应于MIN_TIMESLICE(默认值为10ms)MAX_TIMESLICE(默认值200ms)之间的时间值。一个任务的优先级越高,该任务的当前时间片所减小的每一个定时器节拍的时间量就越大。当时间量减至 0时,调度器再次补充时间片数据,从新计算有效优先级,并将该任务从新排队到活跃向量(若该任务被归类为交互式的)或超时向量(若该任务被看做是非交互式的)中。 此时, 系统会从活跃数组中分派另外一个任务。 该场景确保了在任何超时任务得到再次运行的机会前,首先执行活跃数组中的任务。若是一个任务被取消调度的话,则在唤醒时不会补充时间片数据,可是其有效优先级可能因为任何 accumulated休眠时间而已经发生变化。若是全部可运行任务都耗尽了本身的时间片并已被移至超时列表中,则应交换超时和活跃向量。 这种技术使得该 O(1)调度器不须要遍历一个可能很长的任务列表(这是 Linux 2.4内核调度器中所需的操做)。这种新型设计的问题是,因为任何潜在的交互行为,如下场景可能会出现:活跃队列继续拥有未被移入超时列表的可运行任务。有 些任务可能由于等待 CPU时间而中止。为了不这个问题, 首先移入超时列表中的任务要老于 STARVATION_LIMIT(10),而且应交换活跃数组和超时数组。
相关文章
相关标签/搜索