首页 > 系统相关 >Linux中的内存回收[二]

Linux中的内存回收[二]

时间:2023-08-29 15:14:27浏览次数:40  
标签:swap list 回收 内存 Linux page 页面

https://zhuanlan.zhihu.com/p/72998605

Linux中的内存回收[一]

在NUMA系统中,如果使用页面cache所带来的的收益超过数据存储在不同zone/node的损失,那么可以选择在当前zone内存不足时不进行回收(以保留页面cache),而是使用其他zone/node的空闲内存。

反之,如果数据的locality更加重要,则应该选择在当前zone进行本地回收。可通过"/proc/sys/vm/zone_reclaim_mode"设置是否优先使用本地回收,该参数的默认值是0,表示不开启本地回收。

不过,对于大多数的UMA系统来说,本地回收则是更普遍的现象。回收的触发大致分为两种。一种是在内存分配时发现空闲内存严重不足,直接启动内存回收操作,这种内存回收被称为"direct reclaim"。在direct reclaim模式下,分配和回收是同步的关系,内存分配的过程会被内存回收的操作所阻塞,增加等待的时间。

对于anonymous page的回收,swap out必然会引起相对慢速的I/O操作。对于page cache的回收,如果是"dirty"的,也需要write back,也会引起I/O操作。Direct reclaim造成内存分配的等待已经让它很不好意思了,为了避免I/O操作加剧等待时间,它通常会选择"clean"的page cache来作为回收的对象。

一个更理想的情况是,我们能在内存压力不那么大的时候,就提前启动内存回收。而且,在某些场景下(比如在interrupt context或持有spinlock时),内存分配根本就是不能等待的。因此,Linux中另一种更为常见的内存回收机制是使用kswapd

每个node对应一个内核线程kswapd,kswapd在被唤醒后将扫描各个zone的内存使用状态,并据此进行必要的内存回收操作,因此kswapd的回收方式又被称为"background reclaim"。至于什么情况下触发direct reclaim,什么情况下又会触发background reclaim,是由内存的"watermark"决定的(关于这一块的详细介绍,请参考这篇文章)。

Kswapd虽然名字中含有"swap",但它不光处理anonymous page的swap out回收,同样处理page cache的回收,而且它还肩负着平衡active list和inactive list的重任,所以被它调用的函数叫做balance_pgdat()。

不管是direct reclaim,还是kswapd,最终都是调用shrink_zone() --> shrink_page_list() 进行回收操作。

static unsigned long shrink_list(enum lru_list lru, unsigned long nr_to_scan,
				 struct lruvec *lruvec, struct scan_control *sc)
{
	if (is_active_lru(lru)) {
		if (inactive_list_is_low(lruvec, is_file_lru(lru), sc, true))
			shrink_active_list(nr_to_scan, lruvec, sc, lru);
		return 0;
	}

	return shrink_inactive_list(nr_to_scan, lruvec, sc, lru);
}

当inactive list中的页面比较少时,shrink_active_list()会从active list尾端转移一部分页面到inactive list中。这里的“少”是相对的,通常物理内存越大,inactive list的页面占比可以越小(页面总数还是增加的),因为inactive list的长度主要是用来减少短时间内refault的(参考上篇文章)。

shrink_inactive_list --> shrink_page_list 将从inactive list尾端移除选定数目的页面,进行释放。

回收一个页面之前,如果该页面当前是被映射的(根据struct page的_mapcount域判断),需要调用try_to_unmap(),通过reserve mapping更改所有指向这个页面的PTEs。对于anonymous page,还需要在swap space中分配slot,并且将这个page标记为dirty的。anonymous page是没有backing store的,从dirty的角度,它可以算是一直dirty的。

前面提到过,不是所有的页面都可以被回收的。如果检测到页面的flag是PG_locked或者是PG_reserved的,则只能跳过。对于正在回写的(flag是PG_writeback的),通常也是放弃回收,有这功夫去等待回写完成,还不如去找链表上其他的clean page。

之后,对于flag是PG_dirty的页面,启动pageout()将这些页面备份或者同步到外部磁盘,这里“备份”针对的是anonymous page,“同步”针对的是page cache。

免死金牌

内存回收算法是根据过去的情况预测未来,而且作为工程应用,融入了很多经验的元素,有可能存在一个进程的页面不断被回收,而这个进程因为需要继续运行又不断申请内存页面的情况,导致大部分的时间耗费在磁盘访问上,而进程无法实质性地运行下去。这样的情况虽然少见,但还是有一定发生的几率。为了保证执行完成,进程可以用申请swap token,拥有了swap token,就可以被内存回收算法暂时豁免(相当于免死金牌),除非,内存实在已经紧张的不行了。

Swap token机制是从linux内核2.6.9版本引入的,整个系统只有一个,用swap_token_mm表示,swap_token_mm指向当前持有swap token的进程的mm_struct。为了保证公平性,每个进程持有swap token的时间是有限的,持有时间由一个定时器控制,定时器timeout后,swap token就会转移给下一个进程。

之后的2.6.20版本引入了更合理的swap token抢占机制:如果一个进程在一段时间内换入的页面较多,说明它可能受到页面换出的影响较大,则这样的进程可以获得在swap token抢占中更高的优先级。随着linux虚拟内存模型的不断演进,swap token机制并不能很好的和cgoups以及NUMA placement融合,因而在3.4版本中被移除了,详情请参考这个patch

壮士断腕

尽管kswapd是很努力的,但它毕竟是周期性执行的,难免出现某个时刻系统中剩余的内存极少,少到可能连回收内存操作本身需要的内存都不够了。这时候只能使出终极武器了,那就是OOM killer,做法是选择一个进程(out_of_memory --> select_bad_process),然后kill掉(oom_kill_process中发送SIGKILL信号),把它占用的内存释放出来,牺牲了这个进程,保全了整个系统。虽然OOM killer有时候可能导致严重的损失,但总比系统完全崩溃要好。这个无辜的bad process(其实应该是victim啦)可不是随便挑的,应该优先选择这些:

  • 占有page frames比较多的,占有的多释放的才多,kill掉才有意义。
  • 静态优先级比较低的。

而不能选择这些:

  • 内核线程,因为内核线程往往执行的都是比较关键的任务。
  • 进程号为1的init进程。如果父进程退出,而它的一个或多个子进程还在运行,那么这些子进程将被init进程托管,如果init进程都挂了,那就……所以啊,kill掉init进程是内核不允许的行为。
  • 直接访问硬件设备的进程,如果强行终结这样的进程,可能将硬件置于一个不确定的状态。

 

参考:

为什么手工drop_caches之后cache值并未减少

 

原创文章,转载请注明出处。

标签:swap,list,回收,内存,Linux,page,页面
From: https://www.cnblogs.com/tomato-haha/p/17664842.html

相关文章

  • linux下给硬盘分区为xfs格式
    linux下给硬盘分区为xfs格式  1、lsblk-f这个命令是查看系统可以识别出的所有分区的文件系统类型2查看fdisk-l   3、把vdb分区 Thepartitiontablehasbeenaltered表示分区表已经更改 5、lsblk查看已经分出来的区 6、mkfs.xfs-f/dev/vdb把vdb格式......
  • linux基本学习
     转自:https://mp.weixin.qq.com/s/4dZ3mbu287pC6mdGOczGAA狂神说java目录管理绝对路径和相对路径我们知道Linux的目录结构为树状结构,最顶级的目录为根目录/。其他目录通过挂载可以将它们添加到树中,通过解除挂载可以移除它们。在开始本教程前我们需要先知道什么是绝对路......
  • Linux高性能服务器编程阅读1:
    Linux:所有东西都是文件。socket也是可读,可写,可控制,可编程的文件描述符。I/O高级函数:1.pipe()和dup()/dup2()pipe()函数创建的两个文件描述符fd[0]和fd[1]分别构成管道的两端,往fd[1]中写入的数据可以从fd[0]中读出。且,fd[1]只能写入数据,fd[0]只能读出数据,不能颠倒。dup和dup2......
  • 01 linux 定时任务之关机
    定时关机例:设置在每天03:00定时关机在Linux系统终端执行以下代码 sudo-s#进入rootsudogedit/etc/crontab#编辑/etc/crontab 在打开的窗口添加以下内容,保存并退出  0003***root/sbin/shutdown-hnow#......
  • linux之将程序改为系统启动&开机自动启动
    1.sudovim/etc/systemd/system/django_ops.service2.填入以下内容[Unit]Description=MyCustomServiceAfter=network.target[Service]ExecStart=/data/soft/django_ops/django_ops.shRestart=always[Install]WantedBy=multi-user.targetViewCode3.所用脚本内......
  • 内存对齐的相关问题
    文章目录一、sizeof与strlen区别二、内存对齐1、面试题--->什么是内存对齐?2、内存对齐的规则3、面试题--->为什么要使用内存对齐(提高访问内存速度)?4、如何修改内存对齐参数三、判断两个结构体是否相等一、sizeof与strlen区别sizeof是运算符,结果在编译时计算,不能返回动态分配的内......
  • Linux 中的内存(cache,buffer,)
    在Linux系统中,为了提高文件系统性能,内核利用一部分物理内存分配出缓冲区,用于缓存系统操作和数据文件,当内核收到读写的请求时,内核先去缓存区找是否有请求的数据,有就直接返回,如果没有则通过驱动程序直接操作磁盘。内存查看当我们使用free-h命令时,会显示如下的信息t......
  • linux swap 内存交换分区 详细介绍
    目录1、什么是SWAP,到底是干嘛的?为什么要进行内存回收?会回收的两种内存2、swappiness到底是用来调节什么的?那么这个swappiness到底起到了什么作用呢?3、kswapd什么时候会进行swap操作?4、什么是内存水位标记?(watermark)相关参数设置swap的相关操纵命令5、swap分区的优先级......
  • linux(ubuntu)能ping ip,不能ping域名。无法解析域名DNS指向127.0.0.53问题处理
    故障现象:无法上网。ping互联网ip地址能通信,ping域名无法解析。用nslookupwww.qq.com返回127.0.0.53无法解析的问题。重启无法解决。编辑/etc/resolved.conf配置文件dns写的127.0.0.53.直接添加新的dns,果reboot重启之后,还是原来的内容不变首先修改/etc/systemd/resolved.conf文件......
  • 记项目中的一次内存泄漏问题
    最近在使用QT开发一个大型项目,内存占用可能有点多,其中在vector.push_back()的时候报了一个segmentfault错误,感到很奇怪。因为平时遇到段错误,大多数情况下都是数组越界(使用[]访问元素),而vector的push_back()函数,在添加元素时,如果空间不足,会先向内存申请原来空间的两倍大小的新的空间......