ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

Linux之内存管理

2021-05-30 23:57:20  阅读:209  来源: 互联网

标签:pagecache 管理 程序 内存 内核 Linux 数据 dirty


前言

本篇内存管理学习总结为后面学习I/O的前置。关于I/O我们常听的词都有磁盘I/O、网络I/O、BIO、NIO、多路复用、epoll、mmap、零拷贝、顺序/随机读写,如需深入了解需要有机组的部分知识。楼主大学专业软件工程,有开机组的课程,不过毕业后全部交给老师了。那就先从内存管理开始吧。收!!!开始上干货。

一、内存管理的发展历程

在DOS时代,受到内存大小的限制,同一个时间只能有一个进程在运行。
在Windows 9X时代,增大了机器的内存,此时进程是可以通过内核的调度实现多进程同时运行,但是也存在两个致命的问题:

  • 内存无限增长,直至撑爆;
  • 互相打扰。多个进程之间共用物理内存,A进程可以修改B进程运行的数据、用户态可以随意修改内核态的数据;

为解决以上两个问题,分别采用了内存分页虚拟内存,至此诞生了现在的内存管理模型。

1.1 内存分页(内存撑爆)

假设早期在使用QQ时,会将QQ整个程序所有的文件全部加载的内存中,想想我们再使用一个程序时不会一次性需要程序的所有文件,可不可以程序准备一个文件列表并且每个文件的大小相同,需要用到时候再按照列表将需要的文件加载到内存中,如此可以大大的节省内存空间。其实现在的操作系统就是按照这种设计思路实现的。
如今程序在物理内存都是被分割成N多个的4K内存空间,这4K的内存空间我们可以称之为pagecache,程序需要用到那一块,再加载那一块。如果内存已满则将最不常用的的数据放至SWAP分区。这就是著名的LRU缓存过期算法(哈希表 + 双向链表 O(1)),基本上涉及到缓存的都会有用到此算法,诸如ehcache、redis、mysql的buffer等。

1.2 虚拟内存(互相打扰)

鉴于早期A程序可以随意修改B程序内存的数据,则在物理内存之上增加了一层虚拟文件系统的介质,程序不可以直接去操作物理内存,内存数据的调度需要交由内核去管控,每个进程都虚拟的独占整个CPU,如此一来,进程与进程是完全隔离的。此介质在Linux中称之为VFS。
在这里插入图片描述
每个进程的虚拟内存结构都如上图,在每个区域中都是由多个pagecache组成。
那么既然有了虚拟地址,物理地址和虚拟地址之间的映射时如何完成的?

  1. Main方法开始执行时,代码片段肯定是在数据区,在数据区上会有一个内存地址的下标,称为逻辑地址或者偏移量;
  2. 整个虚拟空间在内存中也是有一个下标,称为基地址。段的基地址+偏移量=线性地址;
  3. 再通过OS和MMU(硬件)完成线性地址到物理地址的转换;

下图看看打开程序QQ的一个逻辑图;(没鼠标画图真的蛋疼!!!)
在这里插入图片描述

在来看两个问题:

  1. 当程序获取虚拟内存中的pagecache不存在时,数据又是如何被内核加载的呢?
  2. 虚拟内存中的pagecache数据的更新存储又是通过什么机制去完成的?

带着疑问我们来细看Linux 的VFS虚拟文件系统是如何实现。

二、VFS虚拟文件系统

2.1 inode id

每个VFS中都有一个唯一的inode id,用来区别标识不同进程的VFS。

2.2 fd文件描述符

众所周知,linux一切皆文件。程序在运行过程中内核是通过fd来访问文件的,在fd上记录了已打开文件的索引信息。其本质就是索引,内核为每个进程维护该进程打开文件的记录表。其实可以理解为java中的迭代器模式中的iterator,每个线程对集合进行遍历,其中的游标信息都是由各自线程自行维护。

2.2.1 linux文件类型

之前记录的笔记直接copy过来,还是比较全面的。
在这里插入图片描述

2.2.2 输入输出表示

在这里插入图片描述

2.2.3 查看fd的信息

在linux中,如需查看当前bash的fd信息,可以通过如下操作,即可查看当前进程记录的fd信息。
在这里插入图片描述
也可以通过命令lsof -p pid查看fd的具体的描述信息。
在这里插入图片描述
从图中列出了fd的类型、读取游标、输入输出情况。

2.3 pagecache

2.3.1 data load

前面提到了数据是需要时被加载,那下面先来看看pagecache是如何被加载的?
在这里插入图片描述

  1. APP读取fd9;
  2. 触发80中断(软中断);
  3. CPU收到中断信息,由用户态切到内核态;
  4. 内核中查看fd9是否存在,存在则返回;当不存在时触发缺页异常;
  5. 从磁盘中获取pagecache的信息;
  6. 由DMA完成数据返回的过程,在此期间程序进入挂起状态;
  7. DMA数据copy完成后,CPU会收到DMA中断信号,恢复线程,程序进入就绪状态;

2.3.2 中断

中断是硬件跟操作系统打交道的一种机制。在操作系统中维护了1个字节的中断向量表,例如 0x01 键盘输入等。在我们程序软件中如果需要调用内核函数,都需要通过0x80中断去实现,这也就是著名的软中断,有软件发出的中断信息。
程序发出中断信号,由操作系统函数system call()按照中断信号找到相应的系统调用函数并去执行。
实例:java读取文件

  1. jvm read();
  2. c库 read();
  3. 内核空间;
  4. system call();
  5. sys_read();

2.3.3 data save

在pagecache中有一个标识用来标识当前pagecache的状态。当应用程序把当前页修改后,会被标识为dirty。dirty数据刷新至磁盘有两种方式:

  1. 用户自己调用flus直接写入磁盘;
  2. 由内核决定写入磁盘的方式;
    • 固定时间刷新至磁盘;
    • 占用物理内存的百分比;

通过cat /proc/vmstat |grep dirty查看系统dirty数据情况;在这里插入图片描述
针对dirty数据默认的刷新阈值,通过命令sysctl -a |grep dirty即可查看。需要根据实际的业务场景进行调优;
在这里插入图片描述
以上参数都是针对内存中dirty数据刷写的处理阈值,其中包含三个纬度:

  1. 内存百分比;
  2. 固定字节;
  3. 固定刷新时间;
    当dirty数据达到阈值时:
  4. 当物理内存充足时,会直接触发dirty数据直接写磁盘;
  5. 当物理内存不足时,会触发LRU淘汰机制。在淘汰中会判断当前数据是否为dirty数据,如果是dirty数据,则刷新磁盘并淘汰;反之直接淘汰。

其中参数中包含background意为开启一个新的线程去处理,不会阻塞新的写入。反之阻塞程序写的写入。

示例:
vm.dirty_background_ratio = 10 : 当内核中的dirty数据达到物理内存的10%,开启一个新的线程执行以上策略;
vm.dirty_ratio = 30:当内核中的dirty数据达到物理内存的30%,则会阻塞新的写入,并执行以上策略;
以下单位: 1/100秒
vm.dirty_expire_centisecs = 3000 : dirty数据的生命周期,dirty数据的超时回刷时间,表示dirty数据可以存30秒;
vm.dirty_writeback_centisecs = 500 : 每5秒将dirty数据刷新至磁盘;

标签:pagecache,管理,程序,内存,内核,Linux,数据,dirty
来源: https://blog.csdn.net/weixin_41455443/article/details/117400849

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有