linux内存之swap

简介

swap是磁盘上一块存储空间。当系统内存使用超过一定值的时候,操作系统就会启动内核进程kswapd,kswapd将部分内存数据置换到swap,从而释放一部分内存出来。swap让进程可以使用超过物理内存大小的内存空间。由于swap是磁盘上的一块空间,所以其读写性能和内存差了1000~10000倍。

功能

如果没有swap的存在,那么一旦进程使用超过物理内存大小的空间,进程就会被oom机制(操作系统为了保证自己能正常运行,强制结束应用进程)强制结束。有了swap的存在,运维人员可以监控swap的使用情况,然后有计划的调整服务器内存。如果开发人员写的代码存在内存泄漏的问题,而被泄漏的内存很可能长时间不被访问,有很大概率被置换到swap,所以swap可以延缓内存泄漏带来的内存耗尽问题。

原理

操作系统将内存分为不同的zone,每个zone管理一片内存区域,/proc/zoneinfo有各个zone管理的内存块信息,每个zone的信息里面有pages free,low,high,min,pages free是zone的空闲页(4KB)数量,low,high,min是三个水位线(见下图)。当某个zone的pages free低于low,kswapd进程就会被唤醒,kswapd扫描内存并将部分内存数据置换到swap(简称为swap out),导致pages free增加;当pages free高于high,内核进程kswapd进入睡眠状态,停止swap out。被swap out到磁盘上的swap块上面的内存数据可能需要被应用程序访问,数据又会被读取到内存(简称为swap in)。通过命令vmstat可以监控到系统的swap out和swap in行为。min的值就是当某个zone的pages free低于min的时候,就会触发oom(为了避免操作系统崩溃,强制结束应用进程),大致过程如下图。

swap.png

常用命令

free -m  #查看swap大小
vmstat -w -t 1  #观察so,si列(swap out,swap in),当这两列
               #持续不为0,意味着内存不够用了,需要排查应用
               #是否存在泄漏,是否需要扩服务器内存
sar -B 1  #如果pgscank大于0,那说明kswapd在工作
         #如果pgsteal不为0,说明有内存被回收
cat /proc/zoneinfo
cat /proc/meminfo
cat /proc/[pid]/status   #VmSwap代表进程有多少内存在磁盘swap分区上

#redhat8创建swap  
lvcreate -L 8G -n swaplv2  rootvg  #创建lv
mkswap /dev/rootvg/swaplv2  #创建swap
swapon /dev/rootvg/swaplv2   #激活swap
free -g  #查看free

测试

写个程序分配内存,再写个程序监控zone的pages使用情况

//sbrk.c 
//通过系统调用sbrk获取内存
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

//操作系统按页管理内存,每个页4KB
#define PG  4096

//获取n个页,需要对获取的内存写点数据,否则
//无法实际占用物理内存
void getm(int n){
    void *ds=sbrk(n*PG);
    if(ds==(void*)-1){
        printf("Out of Memory\n");
        return 0;
    }
    void *end=ds+n*PG;
    for(;;ds+=PG){
        if(ds>=end) break;
        *(int *)(ds)=1;
    }
}

int num=0;
int main(int argc,char *argv[]){
    int def=5000; //默认每秒分配5000个页
    if(argc == 2){
        def=atoi(argv[1]);  //可以传递一个参数设置每秒分配多少页
    }
    while(1){
        getm(def);
        num+=def;
        printf("%d\n",num);
        sleep(1);
    }
    return 0;
}
#!/usr/bin/python3
#通过/proc/zoneinfo观察zone内存状态
import re
import datetime
import sys
import time


def getnamelen():
    global namelen
    namelen=0
    fname='/proc/zoneinfo'
    f=open(fname,'r')
    fcontent=f.read()    
    pattern='^Node.*'
    zonelist=re.findall(pattern,fcontent,flags=re.M)
    namelen=len(zonelist[0])
    f.close()

def pzoneinfo():
    ISOTIMEFORMAT='%m-%d %H:%M:%S'
    times=datetime.datetime.now().strftime(ISOTIMEFORMAT)
    fname='/proc/zoneinfo'
    f=open(fname,'r')
    fcontent=f.read()
    
   #通过正则表达式提取/proc/zoneinfo里面的zone name,pages free,min,high,low
    pattern='^Node.*'
    zonelist=re.findall(pattern,fcontent,flags=re.M)
    
    pattern='^        min.*?(\d+)'
    min=re.findall(pattern,fcontent,flags=re.M)
    
    pattern='^        high.*?(\d+)'
    high=re.findall(pattern,fcontent,flags=re.M)
    
    pattern='^        low.*?(\d+)'
    low=re.findall(pattern,fcontent,flags=re.M)
    
    pattern='^  pages free.*?(\d+)'
    free=re.findall(pattern,fcontent,flags=re.M)
    
    print("{:^{namelen}}    {:<10} {:<10} {:<10} {:<10} {:<10}".format("zonename","free","high","low","min","time",namelen=namelen))
    zonen=0
    for z in zonelist:
        print("{}    {:<10} {:<10} {:<10} {:<10} {}".format(z,free[zonen],high[zonen],low[zonen],min[zonen],times))
        zonen=zonen+1
    totalfree=0
    for f in free:
        totalfree+=int(f)
    print("{:^{namelen}}    {:<10}".format("total free",str(totalfree),namelen=namelen)) 


if __name__=='__main__':
    getnamelen()
    if len(sys.argv)==1:
        print(namelen)
        pzoneinfo()
    elif len(sys.argv)==2:
        ss=int(sys.argv[1])
        while(1):
            pzoneinfo()
            time.sleep(ss)

执行程序

gcc sbrk.c -o sbrk
./sbrk  10000  #根据自己测试服务内存大小调整内存获取速度
python3 watchzone.py  #开新的窗口执行
vmstat -w -t 1 #开新的窗口执行
sar -B 1  #开新的窗口执行

当vmstat -w -t 1观察到si so持续不为0就可以中断各个窗口程序了


vmstat.png
watchzone.png
  • 从vmwat.png可以观察到从11:19:43开始出现so,直接定位watchzone.png的11:19:43左右的数据
  • 从watchzone.png可以看到11:19:42之前total free是每秒减少10000,但是11:19:43的时候total free只减少了4000,猜测是kswapd回收了近6000个页(将6000和vmstat的so对比,存在一定误差,可以接受)
  • 对比free列和low列的值,发现11:19:42的free非常接近low,分配10000个页必然触发低于low

swappiness

除了少部分特殊用途的内存,剩余的内存可以分为两种用途:缓存文件,动态分配(malloc函数);swappiness的作用是调整kswapd回收内存时候是偏向回收缓存文件的内存还是动态分配的内存。文件服务器建议设置较大的swappiness,而不太依赖文件读写的建议设置较小的swappiness。

参考

redhat swappiness
kernel doc

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容