代码编织梦想

1回收哪些页面

Page cache;

用户地址空间的内存映射页面;

Slab缓存:如dentry和inode cache;

匿名页:进程堆栈和mmap匿名映射内存区;回收前先换置到swap;

2何时回收

Kswapd定期唤醒:当系统空闲内存小于阈值则进行页面回收;

直接页面回收:假设操作系统需要通过伙伴系统为用户进程分配一大块内存,或者需要创建一个很大的缓冲区,而当时系统中的内存没有办法提供足够多的物理内存以满足这种内存请求,这时候,操作系统就必须尽快进行页面回收操作;

OS尝试内存回收后仍无法获取足够页面,则调用find_bad_process并进行OOM kill;

3如何回收

基于LRU算法;每个zone维护两个LRU链表

struct zone {

……

spinlock_t                   lru_lock;

struct list_head   active_list;

struct list_head     inactive_list;

unsigned long                  nr_active;

unsigned long                  nr_inactive;

……

}

PG_active/PG_referenced用于标识页面活跃度,前者标识页面时活跃的;后者表示页面最近是否被访问过,每访问一次便会置位;

注:假如只是用一个标志符,在页面被访问时,置位该标志符,之后该页面一直处于活跃状态,如果操作系统不清除该标志位,那么即使之后很长一段时间内该页面都没有或很少被访问过,该页面也还是处于活跃状态。为了能够有效清除该标志位,需要有定时器的支持以便于在超时时间之后该标志位可以自动被清除。然而,很多Linux支持的体系结构并不能提供这样的硬件支持,所以Linux中使用两个标志符来判断页面的活跃程度。

Linux依据这两个字段将page在active_list和inactive_list之间移动;

4de7b6f84d3d69e9163399c4a4a268ad.png

注:1表示函数mark_page_accessed(),2表示函数page_referenced(),3表示函数activate_page(),4表示函数shrink_active_list()

84f15e35919710da0c1969fa4a0a505a.png

不管是kswapd还是直接页面回收,最终都调用shrink_slab和shrink_zone;

直接页面回收:反复调用这两个函数,若特定循环次数内没能成功释放N个page,则调用OOM killer;

Kswapd:对每个zone都调用shrink_zone();

3.1 Shrink_slab原理

先向操作系统内核注册shrinker函数,会在内存较少的时候主动释放一些该磁盘缓存占用的空间。

函数shrink_slab()会遍历shrinker链表,从而对所有注册了shrinker函数的磁盘缓存进行处理。

注册shrinker是通过函数set_shrinker()实现的,解除shrinker注册是通过函数remove_shrinker()实现的。当前,Linux操作系统中主要的shrinker函数有如下几种:

shrink_dcache_memory():该shrinker函数负责dentry缓存。

shrink_icache_memory():该shrinker函数负责inode缓存。

mb_cache_shrink_fn():该shrinker函数负责用于文件系统元数据的缓存。

3.2 Shrink_zone原理

1通过shrink_active_list()将页面从active移到inactive

list;

2调用shrink_inactive_list()将inactive

list的页放入临时链表,最终调用shrink_page_list()回收

ca913b2905d8a0cfe71efa571e49d946.png

3.2.1

Swappiness的意义

上文提到的shrink_zone()会调用shrink_lruvec(),而active/inactive list又各分为anon匿名页和file cache映射页链表,总计4个LRU;

而swappines只针对anon page,即便其为0也有可能执行swap。

vmscan.c中的get_scan_coun()

1.首先如果系统禁用了swap或者没有swap空间,则只扫描file

based的链表,即不进行匿名页链表扫描

代码:

if (!sc->may_swap || (get_nr_swap_pages() <= 0)) {

scan_balance = SCAN_FILE;

goto out;

}

2.如果当前进行的不是全局页回收(cgroup资源限额引起的页回收),并且swappiness设为0,则不进行匿名页链表扫描,

代码:

if (!global_reclaim(sc) && !vmscan_swappiness(sc)) {

scan_balance = SCAN_FILE;

goto out;

}

3.如果进行链表扫描前设置的priority(这个值决定扫描多少分之一的链表元素)为0,且swappiness非0,则可能会进行swap

代码:

if (!sc->priority && vmscan_swappiness(sc)) {

scan_balance = SCAN_EQUAL;

goto out;

}

4.如果是全局页回收,并且当前空闲内存和所有file based链表page数目的加和都小于系统的high watermark,则必须进行匿名页回收,则必然会发生swap

代码:

anon  = get_lru_size(lruvec,

LRU_ACTIVE_ANON) +

get_lru_size(lruvec,

LRU_INACTIVE_ANON);

file  = get_lru_size(lruvec,

LRU_ACTIVE_FILE) +

get_lru_size(lruvec,

LRU_INACTIVE_FILE);

if (global_reclaim(sc)) {

free = zone_page_state(zone,

NR_FREE_PAGES);

if (unlikely(file + free <=

high_wmark_pages(zone))) {

scan_balance = SCAN_ANON;

goto out;

}

}

5.如果系统inactive file链表比较充足,则不考虑进行匿名页的回收,即不进行swap

代码:

if (!inactive_file_is_low(lruvec)) {

scan_balance = SCAN_FILE;

goto out;

}

注:每个zone有min/low/high 3个值,而high watermark指的是最后一个,这3个值依据vm.min_free_kbytes设置

3.2.2反向映射

回收物理页前需要解除所有关联该页的页表项,而共享内存中的页可能被多个进程引用,因此需要一种机制快速定位页表项;

2.4要遍历所有进程;

2.5引入反向映射,每个物理页维护一个页表项链表;

2.6引入基于对象的反向映射,每个物理页设置一个反向映射链表,链表节点为vm_area_struct结构,其通过mm_struct找到pgd进而找到相应页表项;

struct page {

atomic_t _mapcount; --初始值是-1,每增加一个使用者,该计数器加1

union {

……

struct {

……

struct address_space *mapping; --如果最低位置位,为指向anon_vma结构(用于匿名页面)的指针;否则为address_space指针(用于基于文件映射的页面)。

};

……

};

对于匿名页面来说,页面虽然可以是共享的,但是一般情况下,共享匿名页面的使用者的数目不会很多;而对于基于文件映射的页面来说,共享页面的使用者的数目可能会非常多,使用优先级搜索树这种结构可以更加快速地定位那些引用了该页面的虚拟内存区域。操作系统会为每一个文件都建立一个优先级搜索树,其根节点可以通过结构address_space中的i_mmap字段获取。

710ee5bcb1ddd9c4a13c8b16aad8105d.png

注:LRU缓存

页面根据其活跃程度会在active链表和inactive链表之间来回移动,如果要将某个页面插入到这两个链表中去,必须要通过自旋锁以保证对链表的并发访问操作不会出错。为了降低锁的竞争,Linux提供了一种特殊的缓存:LRU缓存,用以批量地向LRU链表中快速地添加页面。有了LRU缓存之后,新页不会被马上添加到相应的链表上去,而是先被放到一个缓冲区中去,当该缓冲区缓存了足够多的页面之后,缓冲区中的页面才会被一次性地全部添加到相应的LRU链表中去。

LRU缓存用到了pagevec结构,如下所示:

struct pagevec {

unsigned long nr;

unsigned long cold;

struct page *pages[PAGEVEC_SIZE];

};

lru_cache_add()和lru_cache_add_active()。前者用于延迟将页面添加到inactive链表上去,后者用于延迟将页面添加到active链表上去。这两个函数都会将要移动的页面先放到页向量pagevec中,当pagevec满了(已经装了14个页面的描述符指针),pagevec结构中的所有页面才会被一次性地移动到相应的链表上去。

参考资料

缓存回收策略 以及 回收算法_zh521zh的博客-爱代码爱编程

缓存回收策略 1  基于空间 即设置缓存的【存储空间】,如设置为10MB,当达到存储空间时,按照一定的策略移除数据。 2  基于容量 基于容量指缓存设置了【条目的最大值】,当缓存的条目超过最大大小,则按照一定的策略将旧数据移除。 3  基于时间 TTL(Time To Live ):存活期,即缓存数据从缓存中创建时间开始直到它到期

linux操作系统之进程 子进程回收_weixin_40878579的博客-爱代码爱编程

回收子进程主要有两个函数:wait(),waitpid()。 一个进程在终止时会关闭所有文件描述符,释放在用户空间分配的内存,但它的PCB还保留着,内核在其中保存了一些信息:如果是正常终止则保存着退出状态,如果是异常终止则

77%的linux运维都不懂的内核问题_weixin_33929309的博客-爱代码爱编程

前言 之前在实习时,听了 OOM 的分享之后,就对 Linux 内核内存管理充满兴趣,但是这块知识非常庞大,没有一定积累,不敢写下,担心误人子弟,所以经过一个一段时间的积累,对内核内存有一定了解之后,今天才写下这篇博客,记录以及分享。 【OOM - Out of Memory】内存溢出 内存溢出的解决办法: 1、等比例缩小图片 2

linux基础——进程的退出及资源回收-爱代码爱编程

文章目录 进程的退出returen 和 exit代码示例注册进程结束调用函数代码示例(on_exit):atexit代码示例(atexit)进程资源的回收代码示例wait回收进程资源代码示例waitpid代码示例给指定进程发送信号(kill)僵尸进程代码示例在进程的虚拟地址空间加载新的映像代码示例使用system启动新的可执行程序代码示例 进程

linux swap 内存交换分区 详细介绍-爱代码爱编程

目录 1、什么是SWAP,到底是干嘛的? 为什么要进行内存回收? 会回收的两种内存 2、swappiness到底是用来调节什么的? 那么这个swappiness到底起到了什么作用呢? 3、kswapd什么时候会进行swap操作? 4、什么是内存水位标记?(watermark) 相关参数设置 swap的相关操纵命令 5、swap分区的优先

linux进程资源回收,有关linux线程资源回收的有关问题-爱代码爱编程

有关linux线程资源回收的问题 使用linux c编程的,开启一个线程,这个线程中申请了一些资源。如果需要这个线程马上取消并且回收申请的资源怎么办啊? 前面使用了pthread_cancel()这个函数,设置了取消点,可是这个也不能立刻就取消回收资源啊啊。。。还有没有其他方法啊? ------解决方案--------------------

linux内存强制回收free的内存,【转】Linux系统内存监控全面讲解之free命令-爱代码爱编程

在Windows系统中查看内存的使用情况很简单,想必大家都已经耳熟能详了,那么在Linux系统如何查看内存使用情况呢?下面和大家分享在Linux系统下查看内存使用情况的free命令: [root@scs-2 tmp]# free total used free shared buffers cached Mem: 3266180 3250004 1

linux stop进程不回收,Linux 进程中 Stop, Park, Freeze-爱代码爱编程

http://kernel.meizu.com/linux-process-stop.html 在调试内核的时候,经常会碰到几个相近的概念:进程 stop、进程 park、进程 freeze。这几个名词看起来都是停止进程,那么他们之间的区别和应用场景在分别是什么呢?下面就来分析一番。 本文的代码分析基于 Linux kernel 3.18.22,最

linux回收站机制,Linux回收站机制实现过程及用法解析-爱代码爱编程

说明: 1. ~/.Trash就是以后被删除的文件和文件夹移动到的地方,也就是回收站 2. $confirm 是实现验证的意思,也就是最后在文件中就是$confirm。其中$@一样 3. 上面的作用,说白了就是命令rm 的重命名。 使用语法: rm(删除),ur(撤销),rl(列出回收站),cleartrash(清空回收站)命令了。 #删除

linux 强制回收fd,深入学习fd泄露问题-爱代码爱编程

最近遇到一个应用闪退的问题,开始发现问题时没有找到明显的问题复现步骤,单独操作应用里的所有功能都没有发生该问题,而一旦进行煲机测试半小时后出现了。光从应用层的log也分析不出问题的原因来,这个问题拖了很长一段时间,真是惭愧!煲机的业务逻辑是通过PC程序循环发送指令到车机板子上,控制车机上的测试程序,调起测试case执行硬件模块的测试。 问题分析 分

android 杀死进程回收资源,Android之进程回收机制LMK(Low Memory Killer)-爱代码爱编程

熟悉Android系统的童鞋都知道,系统出于体验和性能上的考虑,app在退到后台时系统并不会真正的kill掉这个进程,而是将其缓存起来。打开的应用越多,后台缓存的进程也越多。在系统内存不足的情况下,系统开始依据自身的一套进程回收机制来判断要kill掉哪些进程,以腾出内存来供给需要的app, 这套杀进程回收内存的机制就叫 Low Memory Kille

Linux线程退出、资源回收、资源清理的方法-爱代码爱编程

先说明线程中要回收哪些资源,理解清楚了这点之后在思考资源回收的问题。 1、子线程创建时从父线程copy出来的栈内存;   线程退出有多种方式,如return,pthread_exit,pthread_cancel等;线程分为可结合的(joinable)和 分离的(detached)两种,如果没有在创建线程时设置线程的属性为PTHREAD_CREATE_