加入收藏 | 设为首页 | 会员中心 | 我要投稿 武汉站长网 (https://www.027zz.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 服务器 > 搭建环境 > Windows > 正文

5分钟学会两年经验Linux运维都不懂的内核问题

发布时间:2019-09-10 18:03:53 所属栏目:Windows 来源:罗道文的私房菜
导读:副标题#e# 前言 之前在实习时,听了 OOM 的分享之后,就对 Linux 内核内存管理充满兴趣,但是这块知识非常庞大,没有一定积累,不敢写下,担心误人子弟,所以经过一个一段时间的积累,对内核内存有一定了解之后,今天才写下这篇博客,记录以及分享。 【OOM
副标题[/!--empirenews.page--]

前言

之前在实习时,听了 OOM 的分享之后,就对 Linux 内核内存管理充满兴趣,但是这块知识非常庞大,没有一定积累,不敢写下,担心误人子弟,所以经过一个一段时间的积累,对内核内存有一定了解之后,今天才写下这篇博客,记录以及分享。

【OOM - Out of Memory】内存溢出

内存溢出的解决办法:

1、等比例缩小图片

2、对图片采用软引用,及时进行 recycle( ) 操作。

3、使用加载图片框架处理图片,如专业处理图片的 ImageLoader 图片加载框架,还有XUtils 的 BitMapUtils 来处理。

这篇文章主要是分析了单个进程空间的内存布局与分配,是从全局的视角分析下内核对内存的管理;

下面主要从以下方面介绍 Linux 内存管理:

  • 进程的内存申请与分配;
  • 内存耗尽之后 OOM;
  • 申请的内存都在哪?
  • 系统回收内存;

1、进程的内存申请与分配

之前有篇文章介绍 hello world 程序是如何载入内存以及是如何申请内存的,我在这,再次说明下:同样,还是先给出进程的地址空间,我觉得对于任何开发人员这张图是必须记住的,还有一张就是操作 disk ,memory 以及 cpu cache 的时间图。

5分钟学会两年经验Linux运维都不懂的内核问题

当我们在终端启动一个程序时,终端进程调用 exec 函数将可执行文件载入内存,此时代码段,数据段,bbs 段,stack 段都通过 mmap 函数映射到内存空间,堆则要根据是否有在堆上申请内存来决定是否映射。

exec 执行之后,此时并未真正开始执行进程,而是将 cpu 控制权交给了动态链接库装载器,由它来将该进程需要的动态链接库装载进内存。之后才开始进程的执行,这个过程可以通过 strace 命令跟踪进程调用的系统函数来分析。

5分钟学会两年经验Linux运维都不懂的内核问题

这是我上篇博客认识 pipe 中的程序,从这个输出过程,可以看出和我上述描述的一致。

当第一次调用 malloc 申请内存时,通过系统调用 brk 嵌入到内核,首先会进行一次判断,是否有关于堆的 vma,如果没有,则通过 mmap 匿名映射一块内存给堆,并建立 vma 结构,挂到 mm_struct 描述符上的红黑树和链表上。

然后回到用户态,通过内存分配器(ptmaloc,tcmalloc,jemalloc)算法将分配到的内存进行管理,返回给用户所需要的内存。

如果用户态申请大内存时,是直接调用 mmap 分配内存,此时返回给用户态的内存还是虚拟内存,直到第一次访问返回的内存时,才真正进行内存的分配。

其实通过 brk 返回的也是虚拟内存,但是经过内存分配器进行切割分配之后(切割就必须访问内存),全都分配到了物理内存

当进程在用户态通过调用 free 释放内存时,如果这块内存是通过 mmap 分配,则调用 munmap 直接返回给系统。

否则内存是先返回给内存分配器,然后由内存分配器统一返还给系统,这就是为什么当我们调用 free 回收内存之后,再次访问这块内存时,可能不会报错的原因。

当然,当整个进程退出之后,这个进程占用的内存都会归还给系统。

2、内存耗尽之后OOM

在实习期间,有一台测试机上的 mysql 实例经常被 oom 杀死,OOM(out of memory)即为系统在内存耗尽时的自我拯救措施,他会选择一个进程,将其杀死,释放出内存,很明显,哪个进程占用的内存最多,即最可能被杀死,但事实是这样的吗?

今天早上去上班,刚好碰到了一起 OOM,突然发现,OOM 一次,世界都安静下来了,哈哈,测试机上的 redis 被杀死了。

5分钟学会两年经验Linux运维都不懂的内核问题

OOM 关键文件 oom_kill.c,里面介绍了当内存不够时,系统如何选择最应该被杀死的进程,选择因素有挺多的,除了进程占用的内存外,还有进程运行的时间,进程的优先级,是否为 root 用户进程,子进程个数和占用内存以及用户控制参数 oom_adj 都相关。

当产生 oom 之后,函数 select_bad_process 会遍历所有进程,通过之前提到的那些因素,每个进程都会得到一个 oom_score 分数,分数最高,则被选为杀死的进程。

我们可以通过设置 /proc/ /oom_adj 分数来干预系统选择杀死的进程。

5分钟学会两年经验Linux运维都不懂的内核问题

这是内核关于这个oom_adj调整值的定义,最大可以调整为15,最小为-16,如果为-17,则该进程就像买了vip会员一样,不会被系统驱逐杀死了,因此,如果在一台机器上有跑很多服务器,且你不希望自己的服务被杀死的话,就可以设置自己服务的 oom_adj 为-17。

当然,说到这,就必须提到另一个参数 /proc/sys/vm/overcommit_memory,man proc 说明如下:

5分钟学会两年经验Linux运维都不懂的内核问题

意思就是当 overcommit_memory 为0时,则为启发式oom,即当申请的虚拟内存不是很夸张的大于物理内存,则系统允许申请,但是当进程申请的虚拟内存很夸张的大于物理内存,则就会产生 OOM。

例如只有8g的物理内存,然后 redis 虚拟内存占用了24G,物理内存占用3g,如果这时执行 bgsave,子进程和父进程共享物理内存,但是虚拟内存是自己的,即子进程会申请24g的虚拟内存,这很夸张大于物理内存,就会产生一次OOM。

当 overcommit_memory 为1时,则永远都允许 overmemory 内存申请,即不管你多大的虚拟内存申请都允许,但是当系统内存耗尽时,这时就会产生oom,即上述的redis例子,在 overcommit_memory=1 时,是不会产生oom 的,因为物理内存足够。

(编辑:武汉站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读