文章正文
内核编译打开
启用ZRAM
swap机制
启用流程
压缩流程
解压流程
压缩算法
Zsmalloc
ZRAM实现原理详解
一、ZRAM的背景
我们知道在不论是windows还是linux都有一个交换文件,这个交换文件是放在磁盘上的,对于手机设备也是可以这样做的,但是在移动设备中没有像PC机那样的磁盘,而是使用的是flash,Flash的读写寿命远不如磁盘,所以如果使用交换文件,将大大缩短Flash的寿命。
为了不缩短Flash的寿命,程序员想了一个办法就是ZRAM,通过内存存放内存中的数据,只是将数据做了压缩,这样就可以用10M的内存存放20M的数据了,当然这只是一个比方,实际可能压缩的更小,这样同时也不会写入Flash进而影响Flash的寿命。
二、ZRAM的启用
CONFIG_SWAP=y CONFIG_CGROUP_MEM_RES_CTLR=y CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y CONFIG_ZRAM=yCONFIG_ZSMALLOC=y
swapoff /dev/block/zram0 echo 1 >/sys/block/zram0/reset echo 536870912 >/sys/block/zram0/disksize //配置ZRAM大小为512M mkswap /dev/block/zram0 swapon /dev/block/zram0
三、ZRAM的实现
ZRAM的实现需要借助于SWAP以及BLOCK的实现,也就是相关的主要是下面的4层。Kswapd主要是找出anon页,将其交换到zram中,page_fault是在碰到缺页时,将其换出到zram的页换入内存中。
开启ZRAM的必要条件是开启SWAP机制,swap是实现内存“扩大”的核心,对于clean 的filecache类型的数据即使丢弃之后可以通过重新读取获得,但是anon之类的脏数据在丢弃之后就会丢失数据,通过其他设备存放暂时不需要用到的内存的数据,使得内存看起来更大。
在linux下,可以简单的通过proc文件系统获得swap交换区的信息:
在android手机中只有一个swap分区也就是zram。在Swap机制中,最核心的数据结构如下图:
swap_info_struct表示一个交换区,在linux中有一个结构体的数组用于保存系统中所有的交换分区。
struct swap_info_struct* swap_info[MAX_SWAPFILES];
这里我们主要简单介绍一下swap机制启动的过程
Swap机制启动,从代码上看就是个简单的对struct address_space swapper_spaces[MAX_SWAPFILES]这个数据结构数组的初始化,这个启动流程在kswapd_init(void)流程中调用,也就是随着kswapd的启动做了初始化。
swap交换分区的创建,读写流程通过下面的ZRAM的创建以及读写做详细的说明与解释。
我们看到在zram启用的过程中有三个命令,mkswap、swapon、swapoff。分别对应的是zram的创建,开启以及关闭。
这三个命令都是一个应用程序,我们先看mkswap。
从代码中很容易知道,mkswap仅仅是往/dev/block/zram0的前4K写一点配置信息,这也是为什么我们看到在手机刚启动的时候,zram的物理大小是4K。
swapon/swapoff这两个应用程序,仅仅是调用了swapon/swapoff的系统调用,这里就不看应用程序的代码了。
Swapon的系统调用的实现在swapfile.c中,函数中重要的代码如下:
p = alloc_swap_info();//分配一个可用的swap_info的结构体swap_file = file_open_name(name, O_RDWR|O_LARGEFILE, 0);//打开zram文件p->swap_file = swap_file; //swap_info与zram做关联mapping = swap_file->f_mapping;page = read_mapping_page(mapping, 0, swap_file);swap_header = kmap(page);maxpages = read_swap_header(p, swap_header, inode);//读出zram的文件头部的4K//设置swap cgroup的信息,以及处理是否启用frontswapenable_swap_info(p, prio, swap_map, cluster_info, frontswap_map);//启用swap_info,表明这个交换分区可以开始使用了
同样,swapoff系统调用也是在swapfile.c中,这里不再赘述,他的主要功能就是将在zram中的数据换出,将swap cgroup关闭,并释放swap_info结构体。
综上所述,启用流程中最重要的就是,将swap_info与zram做关联,这样就可以将swap的读写,转化为zram的压缩解压流程。
向zram中压缩数据是kswapd这个内核线程,在做shrink的时候触发的,对于file类型的内存页,系统可以直接丢弃,在需要的时候可以在从文件中加载上来,对于匿名页anon,就需要写到zram中,上图中也是比较清楚了。
当碰到需要用交换到ZRAM中的数据的时候,就会触发下面的流程:
首先,我们看第一个过程,handle_mm_fault函数,这个函数是缺页中断触发的。这个函数最终调用了handle_pte_fault函数处理缺页的情况,关键代码如下:
在使用内存的时候首先是判断是否存在页表项,也就是是否在内存中,如果存在说明命中,直接使用这个内存页就好了,中间还有NUMA结构的处理这里忽略。
如果不命中,还需要看这个也是不是被访问过,如果没有访问过就需要通过判断是不是映射页,从而分配相应的内存。如果是访问过的,但是没在内存中,说明这个页被交换出去了,需要从swap中读取该页。
swapin_readahead函数在swap_state.c文件中,这个函数表示正式的转入swap模块中。
bddev_read_page函数是block层的函数,这个函数主要是根据入参做一个转发功能,代码如下:
这里的入参block_device就是zram对应的设备所以这里就调用了zram_rw_page函数,参数是READ,就可以知道是读数据了。
下面的流程与write流程一样,所以直接看zram_bvec_read函数。
ZRAM压缩算法很多,常用的是LZO和LZ4,下面简单的说明一下LZ4的算法。
LZ4是一个非常快的无损压缩算法,压缩速度在单核300MB/S,可扩展支持多核CPU。它还具有一个非常快速的解码器,速度单核可达到和超越1GB/s。
说Zram就不得不说zsmalooc机制,zram的最初的目的就是节省内存,但是在内核中slab机制,不一定能正好使用一个内存页,这样使用zram功能很容易造成页内的碎片。具体的zsmalloc的原理参见https://yq.aliyun.com/articles/4131。
四、ZRAM的影响
ZRAM打开后由于anon page会压缩和解压,必然会带来功耗上的影响,但是只要压缩解压不会颠簸就不会有太大的问题(一般在内存较低的时候会造成一定的影响)。
Feb. 26, 2017, 10:05 p.m. 作者:zachary 分类:Android 阅读(7221) 评论(0)