首页 > 系统相关 >linux物理内存与虚拟内存

linux物理内存与虚拟内存

时间:2023-06-01 12:11:41浏览次数:53  
标签:映射 内存 地址 内核 linux 虚拟内存 物理

1.查看内存占用情况

$ free -m -h
              total        used        free      shared  buff/cache   available
Mem:           7.7G        1.0G        5.9G        385M        780M        6.0G
Swap:          7.4G        1.0G        6.4G

 

2.释放物理内存

(1) 释放内存前先使用sync命令做同步,以确保文件系统的完整性,将所有未写的系统缓冲区写到磁盘中,包含已修改的 i-node、已延迟的块 I/O 和读写映射文件。否则在释放缓存的过程中,可能会丢失未保存的文件。

$ sync

(2) 然后通过修改proc系统的drop_caches清理free的cache

$ echo 3 > /proc/sys/vm/drop_caches

(3) 释放完内存后改回去让系统重新自动分配内存

$ echo 0 > /proc/sys/vm/drop_caches

 

可能会遇到了 "bash: /proc/sys/vm/drop_caches: Permission denied"的问题,即使加上sudo也不行.

原因:重定向符号 “>” 和 ">>" 也是 bash 的命令。使用 sudo 只是让 echo 命令具有了 root 权限,但是没有让 “>” 和 ">>" 命令也具有 root 权限,所以 bash 会认为这两个命令都没有向 drop_caches 文件写入信息的权限。

解决方法:

方法一是利用 "sh -c" 命令,它可以让 bash 将一个字串作为完整的命令来执行,这样就可以将 sudo 的影响范围扩展到整条命令。具体用法如下:

$ sudo sh -c 'echo 3 > /proc/sys/vm/drop_caches'

方法二是利用管道和 tee 命令,该命令可以从标准输入中读入信息并将其写入标准输出或文件中,具体用法如下:

$ echo 3 | sudo tee -a /proc/sys/vm/drop_caches

注意,tee 命令的 "-a" 选项的作用等同于 ">>" 命令,如果去除该选项,那么 tee 命令的作用就等同于 ">" 命令。

参考:https://zhidao.baidu.com/question/1111512531871775459.html

 

3.释放虚拟内存

(1) 关停swap分区(释放虚拟内存):

$ sudo swapoff -a

注意:要保证物理内存剩余量要大于等于swap使用量,否则不能成功释放虚拟内存.(根据内存机制,swap分区一旦释放,所有存放在swap分区的文件都会转存到物理内存上)例如:

$ sudo swapoff -a
swapoff: /dev/sdb8: swapoff failed: Cannot allocate memory

如果成功关停swap分区,可以看到Swap这一行的状态显示全0:

$ free -m -h
              total        used        free      shared  buff/cache   available
Mem:           7.7G        1.7G        4.9G        732M        1.1G        4.9G
Swap:            0B          0B          0B

(2) 再开启swap分区:

$ sudo swapon -a

这时可以看到swap分区free==total,used=0

$ free -m -h
              total        used        free      shared  buff/cache   available
Mem:           7.7G        1.7G        4.8G        740M        1.1G        4.9G
Swap:          7.4G          0B        7.4G

 

关于Linux 虚拟内存和物理内存的理解。

首先,让我们看下虚拟内存:

第一层理解

1. 每个进程都有自己独立的4G内存空间,各个进程的内存空间具有类似的结构

2. 一个新进程建立的时候,将会建立起自己的内存空间,此进程的数据,代码等从磁盘拷贝到自己的进程空间,哪些数据在哪里,都由进程控制表中的task_struct记录,task_struct中记录中一条链表,记录中内存空间的分配情况,哪些地址有数据,哪些地址无数据,哪些可读,哪些可写,都可以通过这个链表记录

3. 每个进程已经分配的内存空间,都与对应的磁盘空间映射

问题:

计算机明明没有那么多内存(n个进程的话就需要n*4G)内存

建立一个进程,就要把磁盘上的程序文件拷贝到进程对应的内存中去,对于一个程序对应的多个进程这种情况,浪费内存!

第二层理解

1. 每个进程的4G内存空间只是虚拟内存空间,每次访问内存空间的某个地址,都需要把地址翻译为实际物理内存地址

2. 所有进程共享同一物理内存,每个进程只把自己目前需要的虚拟内存空间映射并存储到物理内存上。

3. 进程要知道哪些内存地址上的数据在物理内存上,哪些不在,还有在物理内存上的哪里,需要用页表来记录

4.页表的每一个表项分两部分,第一部分记录此页是否在物理内存上,第二部分记录物理内存页的地址(如果在的话)

5. 当进程访问某个虚拟地址,去看页表,如果发现对应的数据不在物理内存中,则缺页异常

6.缺页异常的处理过程,就是把进程需要的数据从磁盘上拷贝到物理内存中,如果内存已经满了,没有空地方了,那就找一个页覆盖,当然如果被覆盖的页曾经被修改过,需要将此页写回磁盘

总结:

优点:

1.既然每个进程的内存空间都是一致而且固定的,所以链接器在链接可执行文件时,可以设定内存地址,而不用去管这些数据最终实际的内存地址,这是有独立内存空间的好处

2.当不同的进程使用同样的代码时,比如库文件中的代码,物理内存中可以只存储一份这样的代码,不同的进程只需要把自己的虚拟内存映射过去就可以了,节省内存

3.在程序需要分配连续的内存空间的时候,只需要在虚拟内存空间分配连续空间,而不需要实际物理内存的连续空间,可以利用碎片。

另外,事实上,在每个进程创建加载时,内核只是为进程“创建”了虚拟内存的布局,具体就是初始化进程控制表中内存相关的链表,实际上并不立即就把虚拟内存对应位置的程序数据和代码(比如.text .data段)拷贝到物理内存中,只是建立好虚拟内存和磁盘文件之间的映射就好(叫做存储器映射),等到运行到对应的程序时,才会通过缺页异常,来拷贝数据。还有进程运行过程中,要动态分配内存,比如malloc时,也只是分配了虚拟内存,即为这块虚拟内存对应的页表项做相应设置,当进程真正访问到此数据时,才引发缺页异常。

补充理解:

虚拟存储器涉及三个概念: 虚拟存储空间,磁盘空间,内存空间

可以认为虚拟空间都被映射到了磁盘空间中,(事实上也是按需要映射到磁盘空间上,通过mmap),并且由页表记录映射位置,当访问到某个地址的时候,通过页表中的有效位,可以得知此数据是否在内存中,如果不是,则通过缺页异常,将磁盘对应的数据拷贝到内存中,如果没有空闲内存,则选择牺牲页面,替换其他页面。

mmap是用来建立从虚拟空间到磁盘空间的映射的,可以将一个虚拟空间地址映射到一个磁盘文件上,当不设置这个地址时,则由系统自动设置,函数返回对应的内存地址(虚拟地址),当访问这个地址的时候,就需要把磁盘上的内容拷贝到内存了,然后就可以读或者写,最后通过manmap可以将内存上的数据换回到磁盘,也就是解除虚拟空间和内存空间的映射,这也是一种读写磁盘文件的方法,也是一种进程共享数据的方法 共享内存

接下来我们来讨论下物理内存:

在内核态申请内存比在用户态申请内存要更为直接,它没有采用用户态那种延迟分配内存技术。内核认为一旦有内核函数申请内存,那么就必须立刻满足该申请内存的请求,并且这个请求一定是正确合理的。相反,对于用户态申请内存的请求,内核总是尽量延后分配物理内存,用户进程总是先获得一个虚拟内存区的使用权,最终通过缺页异常获得一块真正的物理内存。

1.物理内存的内核映射

IA32架构中内核虚拟地址空间只有1GB大小(从3GB到4GB),因此可以直接将1GB大小的物理内存(即常规内存)映射到内核地址空间,但超出1GB大小的物理内存(即高端内存)就不能映射到内核空间。为此,内核采取了下面的方法使得内核可以使用所有的物理内存。

1).高端内存不能全部映射到内核空间,也就是说这些物理内存没有对应的线性地址。不过,内核为每个物理页框都分配了对应的页框描述符,所有的页框描述符都保存在mem_map数组中,因此每个页框描述符的线性地址都是固定存在的。内核此时可以使用alloc_pages()和alloc_page()来分配高端内存,因为这些函数返回页框描述符的线性地址。

2).内核地址空间的后128MB专门用于映射高端内存,否则,没有线性地址的高端内存不能被内核所访问。这些高端内存的内核映射显然是暂时映射的,否则也只能映射128MB的高端内存。当内核需要访问高端内存时就临时在这个区域进行地址映射,使用完毕之后再用来进行其他高端内存的映射。

由于要进行高端内存的内核映射,因此直接能够映射的物理内存大小只有896MB,该值保存在high_memory中。内核地址空间的线性地址区间如下图所示:

从图中可以看出,内核采用了三种机制将高端内存映射到内核空间:永久内核映射,固定映射和vmalloc机制。

2.物理内存管理机制

基于物理内存在内核空间中的映射原理,物理内存的管理方式也有所不同。内核中物理内存的管理机制主要有伙伴算法,slab高速缓存和vmalloc机制。其中伙伴算法和slab高速缓存都在物理内存映射区分配物理内存,而vmalloc机制则在高端内存映射区分配物理内存。

伙伴算法

伙伴算法负责大块连续物理内存的分配和释放,以页框为基本单位。该机制可以避免外部碎片。

per-CPU页框高速缓存

内核经常请求和释放单个页框,该缓存包含预先分配的页框,用于满足本地CPU发出的单一页框请求。

slab缓存

slab缓存负责小块物理内存的分配,并且它也作为高速缓存,主要针对内核中经常分配并释放的对象。

vmalloc机制

vmalloc机制使得内核通过连续的线性地址来访问非连续的物理页框,这样可以最大限度的使用高端物理内存。

3.物理内存的分配

内核发出内存申请的请求时,根据内核函数调用接口将启用不同的内存分配器。

3.1 分区页框分配器

分区页框分配器 (zoned page frame allocator) ,处理对连续页框的内存分配请求。分区页框管理器分为两大部分:前端的管理区分配器和伙伴系统,如下图:

管理区分配器负责搜索一个能满足请求页框块大小的管理区。在每个管理区中,具体的页框分配工作由伙伴系统负责。为了达到更好的系统性能,单个页框的申请工作直接通过per-CPU页框高速缓存完成。该分配器通过几个函数和宏来请求页框,它们之间的封装关系如下图所示。

这些函数和宏将核心的分配函数__alloc_pages_nodemask()封装,形成满足不同分配需求的分配函数。其中,alloc_pages()系列函数返回物理内存首页框描述符,__get_free_pages()系列函数返回内存的线性地址。      3.2 slab分配器

slab 分配器最初是为了解决物理内存的内部碎片而提出的,它将内核中常用的数据结构看做对象。slab分配器为每一种对象建立高速缓存。内核对该对象的分配和释放均是在这块高速缓存中操作。一种对象的slab分配器结构图如下:

可以看到每种对象的高速缓存是由若干个slab组成,每个slab是由若干个页框组成的。虽然slab分配器可以分配比单个页框更小的内存块,但它所需的所有内存都是通过伙伴算法分配的。slab高速缓存分专用缓存和通用缓存。专用缓存是对特定的对象,比如为内存描述符创建高速缓存。通用缓存则是针对一般情况,适合分配任意大小的物理内存,其接口即为kmalloc()。      3.3 非连续内存区内存的分配

内核通过vmalloc()来申请非连续的物理内存,若申请成功,该函数返回连续内存区的起始地址,否则,返回NULL。vmalloc()和kmalloc()申请的内存有所不同,kmalloc()所申请内存的线性地址与物理地址都是连续的,而vmalloc()所申请的内存线性地址连续而物理地址则是离散的,两个地址之间通过内核页表进行映射。vmalloc()的工作方式理解起来很简单:       

1).寻找一个新的连续线性地址空间;       

2).依次分配一组非连续的页框;       

3).为线性地址空间和非连续页框建立映射关系,即修改内核页表;

vmalloc()的内存分配原理与用户态的内存分配相似,都是通过连续的虚拟内存来访问离散的物理内存,并且虚拟地址和物理地址之间是通过页表进行连接的,通过这种方式可以有效的使用物理内存。但是应该注意的是,vmalloc()申请物理内存时是立即分配的,因为内核认为这种内存分配请求是正当而且紧急的;相反,用户态有内存请求时,内核总是尽可能的延后,毕竟用户态跟内核态不在一个特权级。

标签:映射,内存,地址,内核,linux,虚拟内存,物理
From: https://www.cnblogs.com/kn-zheng/p/17448569.html

相关文章

  • linux上限值网速、限值带宽
    Linux操作系统中的流量控制器TC(TrafficControl)用于Linux内核的流量控制,主要是通过在输出端口处建立一个队列来实现流量控制。Linux流量控制的基本原理如下图所示。  接收包从输入接口(InputInterface)进来后,经过流量限制(IngressPolicing)丢弃不符合规定的数据包,由输入多路分......
  • linux Capabiltiy 示例——以前只有root和普通用户两种权限,root的权限太大了,现在有了c
    Capabiltiy示例Capability的设定和清除下面的示例程序给当前的进程设定Capability,最后我们清除掉所设置的Capability,源代码如下:#include<stdio.h>#include<stdlib.h>#include<string.h>#include<sys/types.h>#include<unistd.h>#include<sys/capability.h>exter......
  • LINUX查看进程的4种方法
    进程是在CPU及内存中运行的程序代码,而每个进程可以创建一个或多个进程(父子进程)。查看进程方法第一种:psauxps命令用于报告当前系统的进程状态。可以搭配kill指令随时中断、删除不必要的程序。ps命令是最基本同时也是非常强大的进程查看命令,使用该命令可以确定有哪些进程正在......
  • pythom3.10安装(Linux系统)
    1.下载安装包https://www.python.org/downloads/source/Python-3.10.0.tgz2.解压安装包tar-xvf  Python-3.10.0.tgz3.进行配置 ./configure--prefix=/usr/local/python34.编译安装make&&makeinstall5.建立链接sudoln-s/usr/local/python3/bin/python3.10 /......
  • lINUX iCmp协议
    ICMP是(InternetControlMessageProtocol)Internet控制报文协议。它是TCP/IP协议族的一个子协议,用于在IP主机、路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着重要的......
  • Linux创建socket
    staticconststructnet_proto_familyinet_family_ops={.family=PF_INET,.create=inet_create,.owner=THIS_MODULE,};/**Createaninetsocket.*/staticintinet_create(structnet*net,structsocket*sock,intprotocol,intkern)......
  • Linux 内核 net_proto_family
    staticconststructnet_proto_familyinet_family_ops={.family=PF_INET,.create=inet_create,.owner=THIS_MODULE,};(void)sock_register(&inet_family_ops);/***sock_register-addasocketprotocolhandler*@ops:descriptiono......
  • Linux 内核时钟架构之时钟源读取计数
    前面我们讲到,时钟源是给timekeeping使用的,timekeeping会定时更新,这就依赖timekeeping模块需要读取clocksource的计数,计算时间流逝。然后对时间进行叠加,得到当前时间。 ktime_get()--->tk_core.timekeeperclocksource.read()timekeeping_get_ns()--》read()......
  • linux quota命令使用——应用场景 针对不同的用户设置不同的磁盘访问大小
    quota显示磁盘已使用的空间与限制Linuxquota命令语法quota[选项][用户|组群]命令中各选项的含义如表所示。  Linuxquota命令示例显示用户zhangsan的磁盘使用情况和限制[root@rhel~]#su-zhangsan//以用户zhangsan登录系统[zhangsan@rhel~]$quotaDiskquotasforuse......
  • Linux 内核时钟架构之时钟事件设备与tick_device
    每个CPU定义了一个tick_device,其用于对本cpu使用的时钟事件设备跟踪。也就是说,tick_device是有的,但是这里面有没有clock_event_device我们并不清楚,但是内核在启动时候,如果注册clock_event_device设备,那么内核尝试用时钟事件设备与tick_device设备绑定。这样,两则就关联起来了。......