Linux进程如何访问内存
Linux下,进程并不是直接访问物理内存,而是通过内存管理单元(MMU)来访问内存资源。
原因后面会讲到。
为什么需要虚拟内存地址空间
假设某个进程需要4MB的空间,内存假设是1MB的,如果进程直接使用物理地址,这个进程会因为内存不足跑不起来。
既然进程不是直接访问物理内存,那么进程中涉及的内存地址当然也不是物理内存地址。
而是虚拟的内存地址,虚拟的内存地址和物理的内存地址之间保持一种映射关系,这种关系由MMU进行管理。
每个进程都有自己独立的虚拟地址空间。
什么是MMU
MMU全称是内存管理单元,它将物理内存分割成多个pages,MMU管理进程的虚拟地址空间中的PAGE和物理内存中的PAGE之间的映射关系。
因为是映射,所以随时都可能发生变化,例如某个进程虚拟内存空间中的PAGE1,在不同的时间点,可能出现在物理内存中的不同位置(当发生了页交换时)。
什么是page fault
当进程访问它的虚拟地址空间中的PAGE时,如果这个PAGE目前还不在物理内存中,此时CPU是不能干活的,
Linux会产生一个hard page fault中断。
系统需要从慢速设备(如磁盘)将对应的数据PAGE读入物理内存,并建立物理内存地址与虚拟地址空间PAGE的映射关系。
然后进程才能访问这部分虚拟地址空间的内存。
page fault 又分为几种,major page fault、 minor page fault、 invalid(segment fault)。
major page fault也称为hard page fault, 指需要访问的内存不在虚拟地址空间,也不在物理内存中,需要从慢速设备载入。从swap回到物理内存也是hard page fault。
minor page fault也称为soft page fault, 指需要访问的内存不在虚拟地址空间,但是在物理内存中,只需要MMU建立物理内存和虚拟地址空间的映射关系即可。
(通常是多个进程访问同一个共享内存中的数据,可能某些进程还没有建立起映射关系,所以访问时会出现soft page fault)
invalid fault也称为segment fault, 指进程需要访问的内存地址不在它的虚拟地址空间范围内,属于越界访问,内核会报segment fault错误。
什么是进程的working set
进程的working set是指当前在物理内存中,属于该进程的pages组成的集合。
这个集合中的page数随着系统的运行,可能扩大也可能缩小。
扩大working set是指进程正在访问更多的内存时。
缩小working set是指其他进程正在访问更多的内存,并且整个物理内存的空间不足,需要将某些干净的内存页free掉或者一些脏的内存页swap到交换分区去,如果这个操作设计到当前进程,对当前进程来说就是shrink working set。
缩小working set需要遵循lru (内核的内存老化)算法,并不是随便挑选PAGE进行shrink的。
什么是swap
swap和前面提到的shrink working set有关,如果是干净页(即从读入虚拟地址空间后,没有修改过的页),则直接标记为free,不需要写盘,也不会发生swap。
如果是脏页,那么它需要写盘,或者需要swap 到交换分区。
如何优化swap
建议使用IOPS和读写带宽很高的盘作为SWAP分区,例如PCI-E SSD。
什么时候需要加内存
如果你发现经常发生swap in , out。
说明进程的脏页被换出到swap后,紧接着这些页可能又需要被进程访问,从而这些页需要从swap写入物理内存,发生了hard page fault。
这种情况下,你就需要加内存了,确实是内存不够用了。
hard page fault非常损耗性能,因为发生page fault时,是需要等待的,而且IO通常来说都是比较慢的,容易成为性能瓶颈。
什么是oom, 为什么会发生OOM
前面讲到了shrink working set,是指系统在内存调度时,使用的一种手段,尽可能的让系统能使用有限的内存资源,支撑更多的并发任务。
oom是指系统已经没有足够的内存给进程使用,即能free的都已经free了,能swap out的也已经swap out了,再也不能挤出物理内存的情况。
如果遇到这种情况就会发生OOM,表示系统内存以及不足,Linux会挑选并KILL一些进程,来释放内存。
Linux page相关统计信息
sar命令
Report paging statistics. The following values are displayed:
pgpgin/s
Total number of kilobytes the system paged in from disk per second.
pgpgout/s
Total number of kilobytes the system paged out to disk per second.
fault/s
Number of page faults (major + minor) made by the system per second. This is not a count of page faults that generate I/O, because some page faults can be resolved without I/O.
majflt/s
Number of major faults the system has made per second, those which have required loading a memory page from disk.
pgfree/s
Number of pages placed on the free list by the system per second.
pgscank/s
Number of pages scanned by the kswapd daemon per second.
pgscand/s
Number of pages scanned directly per second.
pgsteal/s
Number of pages the system has reclaimed from cache (pagecache and swapcache) per second to satisfy its memory demands.
%vmeff
Calculated as pgsteal / pgscan, this is a metric of the efficiency of page reclaim. If it is near 100% then almost every page coming off the tail of the inactive list is being reaped. If it gets too low (e.g.
less than 30%) then the virtual memory is having some difficulty. This field is displayed as zero if no pages have been scanned during the interval of time.
一些CASE
如果PostgreSQL 设置了非常大的shared buffer, 为什么会有一段性能低潮(指全力压测时,平时估计感觉不到)
在PostgreSQL shared buffer设得非常大的情况下,Shared buffer作为进程的虚拟地址空间中的一部分,刚启动时这些虚拟地址空间还没有在物理内存中,所以填充过程中会发生page fault,性能较低。
当shared buffer被快速填满后,page fault减少,性能会上来。
例如在压测持续高并发的COPY数据入库时,看这个case https://yq.aliyun.com/articles/8528
在前面几百GB,page fault较多,产生较多的中断(这个case是软的,因为是在shared buffer中进行extend block,么有落盘),性能比shared buffer填满后有一定的差异。
240GB shared buffer,
shared buffer 填满前,2.8GB/s 导入速度
12:13:46 AM pgpgin/s pgpgout/s fault/s majflt/s pgfree/s pgscank/s pgscand/s pgsteal/s %vmeff
12:13:47 AM 8.08 1699410.10 40058.59 0.00 142064.65 137696.97 0.00 137632.32 99.95
12:13:48 AM 15.84 1634396.04 38605.94 0.00 412391.09 411564.36 0.00 411627.72 100.02
22 44 33 0 0 0| 32k 2763M| 239B 364B| 0 0 | 184k 335k
22 44 34 0 0 0| 12k 2791M| 120B 544B| 0 0 | 184k 336k
22 45 33 0 0 0| 12k 2768M| 471B 1198B| 0 0 | 185k 341k
22 44 33 0 0 0| 16k 2764M| 210B 454B| 0 0 | 183k 336k
22 44 34 0 0 0| 16k 2777M| 239B 364B| 0 0 | 185k 341k
shared buffer 填满后, 4.7GB/s导入速度
12:14:40 AM pgpgin/s pgpgout/s fault/s majflt/s pgfree/s pgscank/s pgscand/s pgsteal/s %vmeff
12:14:41 AM 16.33 2831730.61 823.47 0.00 735045.92 725126.53 0.00 724985.71 99.98
12:14:42 AM 12.00 2760160.00 743.00 0.00 738037.00 728352.00 0.00 728370.00 100.00
12:14:43 AM 11.88 2751279.21 326.73 0.00 720171.29 715310.89 0.00 715374.26 100.01
12:14:44 AM 20.00 2788468.00 338.00 0.00 727221.00 720480.00 0.00 720480.00 100.00
12:14:45 AM 12.50 2858970.83 425.00 0.00 753507.29 740266.67 0.00 740166.67 99.99
24 18 56 0 0 1| 28k 4703M| 120B 364B| 0 0 | 366k 655k
24 18 57 0 0 1| 16k 5031M| 892B 994B| 0 0 | 382k 690k
24 19 56 0 0 0| 12k 4833M| 120B 364B| 0 0 | 374k 672k
24 19 56 0 0 1|8192B 4737M| 239B 544B| 0 0 | 371k 668k
24 19 56 0 0 1| 20k 4826M| 120B 454B| 0 0 | 375k 674k