linux为什么主要采用分页机制来实现虚拟存储管理

linux为什么主要采用分页机制来实现虚拟存储管理,第1张

1 分页机制

在虚拟内存中,页表是个映射表的概念, 即从进程能理解的线性地址(linear address)映射到存储器上的物理地址(phisical address).

很显然,这个页表是需要常驻内存的东西, 以应对频繁的查询映射需要(实际上,现代支持VM的处理器都有一个叫TLB的硬件级页表缓存部件,本文不讨论)。

1.1 为什么使用多级页表来完成映射

但是为什么要使用多级页表来完成映射呢?

用来将虚拟地址映射到物理地址的数据结构称为页表, 实现两个地址空间的关联最容易的方式是使用数组, 对虚拟地址空间中的每一页, 都分配一个数组项. 该数组指向与之关联的页帧, 但这会引发一个问题, 例如, IA-32体系结构使用4KB大小的页, 在虚拟地址空间为4GB的前提下, 则需要包含100万项的页表. 这个问题在64位体系结构下, 情况会更加糟糕. 而每个进程都需要自身的页表, 这回导致系统中大量的所有内存都用来保存页表.

设想一个典型的32位的X86系统,它的虚拟内存用户空间(user space)大小为3G, 并且典型的一个页表项(page table entry, pte)大小为4 bytes,每一个页(page)大小为4k bytes。那么这3G空间一共有(3G/4k=)786432个页面,每个页面需要一个pte来保存映射信息,这样一共需要786432个pte!

如何存储这些信息呢?一个直观的做法是用数组来存储,这样每个页能存储(4k/4=)1K个,这样一共需要(786432/1k=)768个连续的物理页面(phsical page)。而且,这只是一个进程,如果要存放所有N个进程,这个数目还要乘上N! 这是个巨大的数目,哪怕内存能提供这样数量的空间,要找到连续768个连续的物理页面在系统运行一段时间后碎片化的情况下,也是不现实的。

为减少页表的大小并容许忽略不需要的区域, 计算机体系结构的涉及会将虚拟地址分成多个部分. 同时虚拟地址空间的大部分们区域都没有使用, 因而页没有关联到页帧, 那么就可以使用功能相同但内存用量少的多的模型: 多级页表

但是新的问题来了, 到底采用几级页表合适呢?

1.2 32位系统中2级页表

从80386开始, intel处理器的分页单元是4KB的页, 32位的地址空间被分为3部分

单元

描述

页目录表Directory最高10位

页中间表Table中间10位

页内偏移最低12位

即页表被划分为页目录表Directory和页中间表Tabl两个部分

此种情况下, 线性地址的转换分为两步完成.

第一步, 基于两级转换表(页目录表和页中间表), 最终查找到地址所在的页帧

第二步, 基于偏移, 在所在的页帧中查找到对应偏移的物理地址

使用这种二级页表可以有效的减少每个进程页表所需的RAM的数量. 如果使用简单的一级页表, 那将需要高达220个页表, 假设每项4B, 则共需要占用220?4B=4MB的RAM来表示每个进程的页表. 当然我们并不需要映射所有的线性地址空间(32位机器上线性地址空间为4GB), 内核通常只为进程实际使用的那些虚拟内存区请求页表来减少内存使用量.

1.3 64位系统中的分页

正常来说, 对于32位的系统两级页表已经足够了, 但是对于64位系统的计算机, 这远远不够.

首先假设一个大小为4KB的标准页. 因为1KB覆盖210个地址的范围, 4KB覆盖212个地址, 所以offset字段需要12位.

这样线性地址空间就剩下64-12=52位分配给页中间表Table和页目录表Directory. 如果我们现在决定仅仅使用64位中的48位来寻址(这个限制其实已经足够了, 2^48=256TB, 即可达到256TB的寻址空间). 剩下的48-12=36位被分配给Table和Directory字段. 即使我们现在决定位两个字段各预留18位, 那么每个进程的页目录和页表都包含218个项, 即超过256000个项.

基于这个原因, 所有64位处理器的硬件分页系统都使用了额外的分页级别. 使用的级别取决于处理器的类型

平台名称

页大小

寻址所使用的位数

分页级别数

线性地址分级

alpha8KB43310 + 10 + 10 + 13

ia644KB3939 + 9 + 9 + 12

ppc644KB41310 + 10 + 9 + 12

sh644KB41310 + 10 + 9 + 12

x86_644KB4849 + 9 + 9 + 9 + 12

vmstat是Virtual Meomory Statistics(虚拟内存统计)的缩写,可对 *** 作系统的虚拟内存、进程、CPU活动进行监控。他是对系统的整体情况进行统计,不足之处是无法对某个进程进行深入分析。vmstat 工具提供了一种低开销的系统性能观察方式。因为 vmstat 本身就是低开销工具,在非常高负荷的服务器上,你需要查看并监控系统的健康情况,在控制窗口还是能够使用vmstat 输出结果。在学习vmstat命令前,我们先了解一下Linux系统中关于物理内存和虚拟内存相关信息。

物理内存和虚拟内存区别:

我们知道,直接从物理内存读写数据要比从硬盘读写数据要快的多,因此,我们希望所有数据的读取和写入都在内存完成,而内存是有限的,这样就引出了物理内存与虚拟内存的概念。

物理内存就是系统硬件提供的内存大小,是真正的内存,相对于物理内存,在linux下还有一个虚拟内存的概念,虚拟内存就是为了满足物理内存的不足而提出的策略,它是利用磁盘空间虚拟出的一块逻辑内存,用作虚拟内存的磁盘空间被称为交换空间(Swap Space)。

作为物理内存的扩展,linux会在物理内存不足时,使用交换分区的虚拟内存,更详细的说,就是内核会将暂时不用的内存块信息写到交换空间,这样以来,物理内存得到了释放,这块内存就可以用于其它目的,当需要用到原始的内容时,这些信息会被重新从交换空间读入物理内存。

linux的内存管理采取的是分页存取机制,为了保证物理内存能得到充分的利用,内核会在适当的时候将物理内存中不经常使用的数据块自动交换到虚拟内存中,而将经常使用的信息保留到物理内存。

要深入了解linux内存运行机制,需要知道下面提到的几个方面:

首先,Linux系统会不时的进行页面交换 *** 作,以保持尽可能多的空闲物理内存,即使并没有什么事情需要内存,Linux也会交换出暂时不用的内存页面。这可以避免等待交换所需的时间。

其次,linux进行页面交换是有条件的,不是所有页面在不用时都交换到虚拟内存,linux内核根据”最近最经常使用“算法,仅仅将一些不经常使用的页面文件交换到虚拟内存,有时我们会看到这么一个现象:linux物理内存还有很多,但是交换空间也使用了很多。其实,这并不奇怪,例如,一个占用很大内存的进程运行时,需要耗费很多内存资源,此时就会有一些不常用页面文件被交换到虚拟内存中,但后来这个占用很多内存资源的进程结束并释放了很多内存时,刚才被交换出去的页面文件并不会自动的交换进物理内存,除非有这个必要,那么此刻系统物理内存就会空闲很多,同时交换空间也在被使用,就出现了刚才所说的现象了。关于这点,不用担心什么,只要知道是怎么一回事就可以了。

最后,交换空间的页面在使用时会首先被交换到物理内存,如果此时没有足够的物理内存来容纳这些页面,它们又会被马上交换出去,如此以来,虚拟内存中可能没有足够空间来存储这些交换页面,最终会导致linux出现假死机、服务异常等问题,linux虽然可以在一段时间内自行恢复,但是恢复后的系统已经基本不可用了。

因此,合理规划和设计linux内存的使用,是非常重要的。

虚拟内存原理:

在系统中运行的每个进程都需要使用到内存,但不是每个进程都需要每时每刻使用系统分配的内存空间。当系统运行所需内存超过实际的物理内存,内核会释放某些进程所占用但未使用的部分或所有物理内存,将这部分资料存储在磁盘上直到进程下一次调用,并将释放出的内存提供给有需要的进程使用。

在Linux内存管理中,主要是通过“调页Paging”和“交换Swapping”来完成上述的内存调度。调页算法是将内存中最近不常使用的页面换到磁盘上,把活动页面保留在内存中供进程使用。交换技术是将整个进程,而不是部分页面,全部交换到磁盘上。

分页(Page)写入磁盘的过程被称作Page-Out,分页(Page)从磁盘重新回到内存的过程被称作Page-In。当内核需要一个分页时,但发现此分页不在物理内存中(因为已经被Page-Out了),此时就发生了分页错误(Page Fault)。

当系统内核发现可运行内存变少时,就会通过Page-Out来释放一部分物理内存。经管Page-Out不是经常发生,但是如果Page-out频繁不断的发生,直到当内核管理分页的时间超过运行程式的时间时,系统效能会急剧下降。这时的系统已经运行非常慢或进入暂停状态,这种状态亦被称作thrashing(颠簸)。

总结:物理内存就是硬件提供的真实的内存,比如我们电脑内存不够了,就会加一个内存条

虚拟内存就是从磁盘上虚拟出来的一块逻辑内存,用做虚拟内存的磁盘空间被称为交换空间(Swap Space

经常使用的文件会优先放在物理内存,不经常使用的文件会放到虚拟内存里面。

linux下的vm(虚拟内存)和windows下的作用是一样的,均是防止真实内存资源不足准备的.

linux的vm相关参数介绍

1. 保证linux有足够的物理内存,可以调整vm的如下参数

vm.min_free_kbytes=409600//默认值是3797,保证物理内存有足够空闲空间,防止突发性换页

vm.vfs_cache_pressure=200;//默认是100,增大这个参数设置了虚拟内存回收directory和i-node缓冲的倾向,这个值越大。越易回收

vm.swappiness=40 //缺省60,减少这个参数会使系统尽快通过swapout不使用的进程资源来释放更多的物理内存

一般在rac的配置环境中配置这三个参数,可以防止换页引起节点短暂无响应,导致节点重启

2. 改善io系统的性能

overcommit_memory = 0

vm.overcommit_ratio = 10 //默认值是50,用于虚拟内存的物理内存的百分比

vm.dirty_ratio = 20 //默认值是40,为了保持稳定,持续的写入,把这个值调整的小一些,经验值是20

vm.dirty_background_ratio //缺省数值是500,也就是5秒,如果系统要求稳定持续的写,可以适当降低该值,把峰值的写 *** 作平均多次,也避免宕机丢失更多的数据

vm.dirty_expire_centisecs //缺省是3000,也就是30秒,如果系统写 *** 作压力很大,可以适当减小该值,但也不要太小;建议设置为 1500

vm的相关参数在/proc/sys目录下

相关命令

sysctl -p //修改vm参数后,运行这个命令可以立即生效

sysctl -a //查看所有的vm参数

与磁盘IO子系统有关的

/proc/sys/vm/dirty_ratio

这个参数控制文件系统的文件系统写缓冲区的大小,单位是百分比,表示系统内存的百分比,

表示当写缓冲使用到系统内存多少的时候,开始向磁盘写出数据。增大之会使用更多系统内

存用于磁盘写缓冲,也可以极大提高系统的写性能。但是,当你需要持续、恒定的写入场合

时,应该降低其数值,一般启动上缺省是 10。下面是增大的方法:

echo '40' >/proc/sys/vm/dirty_ratio

/proc/sys/vm/dirty_background_ratio

这个参数控制文件系统的pdflush进程,在何时刷新磁盘。单位是百分比,表示系统内存的百

分比,意思是当写缓冲使用到系统内存多少的时候,pdflush开始向磁盘写出数据。增大之会

使用更多系统内存用于磁盘写缓冲,也可以极大提高系统的写性能。但是,当你需要持续、恒

定的写入场合时,应该降低其数值,一般启动上缺省是 5。下面是增大的方法:

echo '20' >/proc/sys/vm/dirty_background_ratio

/proc/sys/vm/dirty_writeback_centisecs

这个参数控制内核的脏数据刷新进程pdflush的运行间隔。单位是 1/100 秒。缺省数值是500,

也就是 5 秒。如果你的系统是持续地写入动作,那么实际上还是降低这个数值比较好,这样可

以把尖峰的写 *** 作削平成多次写 *** 作。设置方法如下:

echo "200" >/proc/sys/vm/dirty_writeback_centisecs

如果你的系统是短期地尖峰式的写 *** 作,并且写入数据不大(几十M/次)且内存有比较多富裕,那么应该增大此数值:

echo "1000" >/proc/sys/vm/dirty_writeback_centisecs

/proc/sys/vm/dirty_expire_centisecs

这个参数声明Linux内核写缓冲区里面的数据多“旧”了之后,pdflush进程就开始考虑写到磁盘中去。

单位是 1/100秒。缺省是 3000,也就是 30 秒的数据就算旧了,将会刷新磁盘。对于特别重载的写

*** 作来说,这个值适当缩小也是好的,但也不能缩小太多,因为缩小太多也会导致IO提高太快。建

议设置为 1500,也就是15秒算旧。

echo "1500" >/proc/sys/vm/dirty_expire_centisecs

当然,如果你的系统内存比较大,并且写入模式是间歇式的,并且每次写入的数据不大(比如几十M),

那么这个值还是大些的好。

与网络IO子系统有关的

/proc/sys/net/ipv4/tcp_retrans_collapse

这个参数控制TCP双方Window协商出现错误的时候的一些重传的行为。但是在老的2.6的核

(<2.6.18)里头,这个重传会导致kernel oops,kernel panic,所以,如果出现有

tcp_retrans_*样子的kernel panic,可以把这个参数给设置成0:

echo '0' >/proc/sys/net/ipv4/tcp_retrans_collapse

提高Linux应对短连接的负载能力

在存在大量短连接的情况下,Linux的TCP栈一般都会生成大量的 TIME_WAIT 状态的socket。

你可以用下面的命令看到:

netstat -ant| grep -i time_wait

有时候,这个数目是惊人的:

netstat -ant|grep -i time_wait |wc -l

可能会超过三四万。这个时候,我们需要修改 linux kernel 的 tcp time wait的时间,缩短之,

有个 sysctl 参数貌似可以使用,它是 /proc/sys/net/ipv4/tcp_fin_timeout,缺省值是 60,

也就是60秒,很多网上的资料都说将这个数值设置低一些就可以减少netstat 里面的TIME_WAIT状态,

但是这个说法是错误的。经过认真阅读Linux的内核源代码,我们发现这个数值其实是输出用的,

修改之后并没有真正的读回内核中进行使用,而内核中真正管用的是一个宏定义,在

$KERNEL/include/net/tcp.h里面,有下面的行:

#define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to destroy TIME-WAIT

* state, about 60 seconds */

而这个宏是真正控制 TCP TIME_WAIT 状态的超时时间的。如果我们希望减少 TIME_WAIT 状态的数目

(从而节省一点点内核 *** 作时间),那么可以把这个数值设置低一些,根据我们的测试,设置为 10

秒比较合适,也就是把上面的修改为:

#define TCP_TIMEWAIT_LEN (10*HZ) /* how long to wait to destroy TIME-WAIT

* state, about 60 seconds */

然后重新编译内核,重启系统即可发现短连接造成的TIME_WAIT状态大大减少:

netstat -ant | grep -i time_wait |wc -l

一般情况都可以至少减少2/3。也能相应提高系统应对短连接的速度。

/proc/irq/{number}/smp_affinity

在多 CPU 的环境中,还有一个中断平衡的问题,比如,网卡中断会教给哪个 CPU 处理,

这个参数控制哪些 CPU 可以绑定 IRQ 中断。其中的 {number} 是对应设备的中断编号,

可以用下面的命令找出:

cat /proc/interrupt

比如,一般 eth0 的 IRQ 编号是 16,所以控制 eth0 中断绑定的 /proc 文件名是

/proc/irq/16/smp_affinity。上面这个命令还可以看到某些中断对应的CPU处理的次数,

缺省的时候肯定是不平衡的。

设置其值的方法很简单,smp_affinity 自身是一个位掩码(bitmask),特定的位对应特

定的 CPU,这样,01 就意味着只有第一个 CPU 可以处理对应的中断,而 0f(0x1111)

意味着四个 CPU 都会参与中断处理。

几乎所有外设都有这个参数设置,可以关注一下。

这个数值的推荐设置,其实在很大程度上,让专门的CPU处理专门的中断是效率最高的,比如,

给磁盘IO一个CPU,给网卡一个CPU,这样是比较合理的。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/8323745.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-15
下一篇 2023-04-15

发表评论

登录后才能评论

评论列表(0条)

保存