0

0

Linux 了解内存使用

爱谁谁

爱谁谁

发布时间:2025-07-19 09:06:26

|

1118人浏览过

|

来源于php中文网

原创

1. Linux虚拟内存布局32位Linux系统内存划分:

通常32位linux内核地址空间划分0~3g为用户空间,3~4g为内核空间。

进程寻址空间0~4G进程在用户态只能访问0~3G,只有进入内核态才能访问3G~4G 进程通过系统调用进入内核态每个进程虚拟空间的3G~4G部分是相同的 进程从用户态进入内核态不会引起CR3的改变但会引起堆栈的改变。64位Linux系统内存划分:

目前大部分的操作系统和应用程序并不需要16EB( 2^64 )如此巨大的地址空间, 实现64位长的地址只会增加系统的复杂度和地址转换的成本, 带不来任何好处. 所以目前的x86-64架构CPU都遵循AMD的Canonical form, 即只有虚拟地址的最低48位才会在地址转换时被使用, 且任何虚拟地址的48位至63位必须与47位一致(sign extension). 也就是说, 总的虚拟地址空间为256TB( 2^48 )

在这256TB的虚拟内存空间中:

0000000000000000 - 00007fffffffffff(128TB)为用户空间,参见定义:

#define TASK_SIZE_MAX ((1UL

ffff800000000000 - ffffffffffffffff(128TB)为内核空间.

注意:该地址前4个都是f,这是因为目前实际上只用了64位地址中的48位(高16位是没有用的),而从地址0x0000,7fff,ffff,ffff到0xffff,8000,0000,0000中间是一个巨大的空洞,是为以后的扩展预留的。

而真正的系统空间的起始地址,是从0xffff,8800,0000,0000开始的,参见:

#define __PAGE_OFFSET _AC(0xffff,8800,0000,0000, UL)

32位与64位具体地址分布如下图:

Linux 了解内存使用
2. 用户进程内存结构

top 命令了解进程信息,其中包括内存方面的信息。

正在运行的程序,叫进程。每个进程都有完全属于自己的,独立的,不被干扰的内存空间。此空间,被分成几个段(Segment),分别是Text, Data, BSS, Heap, Stack。用户进程内存空间,也是系统内核分配给该进程的VM(虚拟内存),但并不表示这个进程占用了这么多的RAM(物理内存)。这个空间有多大?命令top输出的VIRT值告诉了我们各个进程内存空间的大小(进程内存空间随着程序的执行会增大或者缩小)。你还可以通过/proc/$pid/maps,或者pmap –d 了解某个进程内存空间都分布,比如:

[root@localhost4 vhosts]# pmap 19254 -x 19254: nginx: master process /usr/local/app/nginx/sbin/nginx Address Kbytes RSS Dirty Mode Mapping 0000000000400000 552 224 0 r-x-- nginx 0000000000689000 72 60 60 rw--- nginx 000000000069b000 56 12 12 rw--- [ anon ] 0000000000950000 652 636 636 rw--- [ anon ] 00000037c3a00000 92 44 0 r-x-- libpthread-2.12.so 00000037c3a17000 2048 0 0 ----- libpthread-2.12.so 00000037c3c17000 4 4 4 r---- libpthread-2.12.so 00000037c3c18000 4 4 4 rw--- libpthread-2.12.so

扩展和设备格式区域 Address: 内存开始地址 Kbytes: 占用内存的字节数(KB) RSS: 保留内存的字节数(KB) Dirty: 脏页的字节数(包括共享和私有的)(KB) Mode: 内存的权限:read、write、execute、shared、private (写时复制) Mapping: 占用内存的文件、或[anon](分配的内存)、或[stack](堆栈) Offset: 文件偏移 Device: 设备名 (major:minor)

Linux 了解内存使用

当fork()或者exec()一个进程的时候,系统内核就会分配一定量的VM给进程,作为进程的内存空间,大小由BSS段,Data段的已定义的全局变量、静态变量、Text段中的字符直接量、程序本身的内存映像等,还有Stack段的局部变量决定。当然,还可以通过malloc()等函数动态分配内存,向上扩大heap。

动态分配与静态分配,二者最大的区别在于:

1. 直到Run-Time的时候,执行动态分配,而在compile-time的时候,就已经决定好了分配多少Text+Data+BSS+Stack。

2.通过malloc()动态分配的内存,需要程序员手工调用free()释放内存,否则容易导致内存泄露,而静态分配的内存则在进程执行结束后系统释放(Text, Data), 但Stack段中的数据很短暂,函数退出立即被销毁。

示例小程序,加深理解:

/* @filename: example-2.c */

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">#include <stdio.h>int main(int argc, char *argv[])  {    char arr[] = "hello world";     /* Stack段,rw--- */    char *p = "hello world";        /* Text段,字符串直接量, r-x--  */    arr[1] = 'l';    *(++p) = 'l';   /* 出错了,Text段不能write */    return 0;}</code>

变量p,它在Stack段,但它所指的”hello world”是一个字符串直接量,放在Text段。 /* @filename:example_2_2.c */

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">#include <stdio.h>#include <stdlib.h>#include <string.h>char *get_str_1(){    char str[] = "hello world";    return str;}char *get_str_2() {    char *str = "hello world";    return str;}char *get_str_3(){    char tmp[] = "hello world";    char *str;    str = (char *)malloc(12 * sizeof(char));    memcpy(str, tmp, 12);    return str;}int main(int argc, char *argv[])  {    char *str_1 = get_str_1();  //出错了,Stack段中的数据在函数退出时就销毁了    char *str_2 = get_str_2();  //正确,指向Text段中的字符直接量,退出程序后才会回收    char *str_3 = get_str_3();  //正确,指向Heap段中的数据,还没free()    printf("%s\n", str_1);    printf("%s\n", str_2);    printf("%s\n", str_3);    if (str_3 != NULL)   {        free(str_3);        str_3 = NULL;    }    return 0;}</code>

函数get_str_1()返回Stack段数据,编译时会报错:

example_2_3.c: In function ‘get_str_1’: example_2_3.c:7: warning: function returns address of local variable

Heap中的数据,如果不用了,应该尽早释放free()。

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h> char data_var  = '1';char *mem_killer(){   char *p;   p = (char *)malloc(1024*1024*4);   memset(p, '\0', 1024*1024*4);   p = &data_var;   //危险,内存泄露   return p;} int main(int argc, char *argv[]){    char *p;    for (;;)    {        p = mem_killer(); // 函数中malloc()分配的内存没办法free()        printf("%c\n", *p);        sleep(20);    }    return 0;}</code>

使用malloc(),特别要留意heap段中的内存不用时,尽早手工free()。通过top输出的VIRT和RES两值来观察进程占用VM和RAM大小。 因为Text, BSS, Data段在编译时已经决定了进程将占用多少VM。可以通过size,知道这些信息:

[root@localhost4 ~]# gcc example_2_3.c -o example_2_3 [root@localhost4 ~]# size example_2_3 text data bss dec hex filename 1503 520 16 2039 7f7 example_2_3

3. 用户进程内存分配malloc

我们在编写程序之际,时常要处理变化数据,无法预料要处理的数据集变化是否大,所以除了变量之外,还需要动态分配内存。GNU libc库提供了二个内存分配函数,分别是malloc()和calloc()。调用malloc(size_t size)函数分配内存成功,总会分配size字节VM(再次强调不是RAM),并返回一个指向刚才所分配内存区域的开端地址。分配的内存会为进程一直保留着,直到你显示地调用free()释放它(当然,整个进程结束,静态和动态分配的内存都会被系统回收)。开发人员有责任尽早将动态分配的内存释放回系统。记住一句话:尽早free()!

我们来看看,malloc()小示例:

/* @filename:example_2_4.c */

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">#include <stdio.h>#include <stdlib.h> int main(int argc, char *argv[]){    char *p_4kb, *p_128kb, *p_300kb;    if ((p_4kb = malloc(4*1024)) != NULL){        free(p_4kb);}    if ((p_128kb = malloc(128*1024)) != NULL) {        free(p_128kb);    }    if ((p_300kb = malloc(300*1024)) != NULL)  {        free(p_300kb);    }    return 0;}</code>

#gcc example_2_4.c –o example_2_4 #strace –t ./example_2_4 … 00:02:53 brk(0) = 0x8f58000 00:02:53 brk(0x8f7a000) = 0x8f7a000 00:02:53 brk(0x8f79000) = 0x8f79000 00:02:53 mmap2(NULL, 311296, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb772d000 00:02:53 munmap(0xb772d000, 311296) = 0

系统调用brk(0)取得当前堆的地址,也称为断点。

通过跟踪系统内核调用,可见glibc函数malloc()总是通过brk()或mmap()系统调用来满足内存分配需求。函数malloc(),根据不同大小内存要求来选择brk(),还是mmap() 128Kbytes是临界值:

1) 小块内存(

2)大块内存,则使用mmap()进行匿名映射(设置标志MAP_ANONYMOUS)来分配内存,与堆无关,在堆之外。

这样做是有道理的,试想:如果大块内存,也调用brk(),则容易被小块内存钉住,必竟用大块内存不是很频繁;反过来,小块内存分配更为频繁得多,如果也使用mmap(),频繁的创建内存映射会导致更多的开销,还有一点就是,内存映射的大小要求必须是“页”(单位,内存页面大小,默认4Kbytes或8Kbytes)的倍数,如果只是为了”hello world”这样小数据就映射一“页”内存,那实在是太浪费了。

跟malloc()一样,释放内存函数free(),也会根据内存大小,选择使用brk()将断点往低处回推,或者选择调用munmap()解除映射。有一点需要注意:并不是每次调用free()小块内存,都会马上调用brk(),即堆并不会在每次内存被释放后就被缩减,而是会被glibc保留给下次malloc()使用(必竟小块内存分配较为频繁),直到glibc发现堆空闲大小显著大于内存分配所需数量时,则会调用brk()。但每次free()大块内存,都会调用munmap()解除映射.

4. 缺页处理

Linux 利用虚拟内存极大的扩展了程序地址空间,使得原来物理内存不能容下的程序也可以通过内存和硬盘之间的不断交换来赢得更多的内存,看起来就像物理内存被扩大了一样。事实上这个过程对程序是完全透明的,程序完全不用理会自己哪一部分、什么时候被交换进内存,一切都有内核的虚拟内存管理来完成。当程序启动的时候,Linux 内核首先检查 CPU 的缓存和物理内存,如果数据已经在内存里就忽略,如果数据不在内存里就引起一个缺页中断(Page Fault),然后从硬盘读取缺页,并把缺页缓存到物理内存里。缺页中断可分为主缺页中断(Major Page Fault)和次缺页中断(Minor Page Fault),要从磁盘读取数据而产生的中断是主缺页中断;数据已经被读入内存并被缓存起来,从内存缓存区中而不是直接从硬盘中读取数据而产生的中断是次缺页中断。

可以通过time 命令可以用来查看某程序第一次启动的时候产生了多少主缺页中断和次缺页中断:

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">$ /usr/bin/time -v date...Major (requiring I/O) page faults: 1Minor (reclaiming a frame) page faults: 260</code>

每次调用malloc(),系统都只是给进程分配线性地址(VM),并没有随即分配页框(RAM)。系统尽量将分配页框的工作推迟到最后一刻—用到时缺页异常处理。这种页框按需延迟分配策略最大好处之一:充分有效地善用系统稀缺资源RAM。

当指针引用的内存页没有驻留在RAM中,即在RAM找不到与之对应的页框,则会发生缺页异常(对进程来说是透明的),内核便陷入缺页异常处理。发生缺页异常有几种情况:

1.只分配了线性地址,并没有分配页框,常发生在第一次访问某内存页。

2.已经分配了页框,但页框被回收,换出至磁盘(交换区)。

3.引用的内存页,在进程空间之外,不属于该进程,可能已被free()。

我们使用一段伪代码来大致了解缺页异常。

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">/* @filename: example_2_5.c */…demo() {    char *p;    if ((p = malloc(1024*100)) != NULL)  // L0:分配了100Kbytes线性地址    {        *p = 't';      // L1         …         *p = 'm';      // L2:过去了很长一段时间,不管系统忙否,长久不用的页框都有可能被回收         p[4096] = 'p';// L3          …        free(p);       //L4if (p == NULL) {*p = 'l';  // L5}    }}…</code>
L0,函数malloc()通过brk()给进程分配了100Kbytes的线性地址区域(VM).然而,系统并没有随即分配页框(RAM)。即此时,进程没有占用100Kbytes的物理内存。这也表明了,你时常在使用top的时候VIRT值增大,而RES值却不变的原因。 L1,通过*p引用了100Kbytes的第一页(4Kbytes)。因为是第一次引用此页,在RAM中找不到与之相对应的页框。发生缺页异常(对于进程而言缺页异常是透明的),系统灵敏地捕获这一异常,进入缺页异常处理阶段:接下来,系统会分配一个页框(RAM)映射给它。我们把这种情况(被访问的页还没有被放在任何一个页框中,内核分配一新的页框并适当初始化来满足调用请求),也称为Demand Paging。 L2,过了很长一段时间,通过*p再次引用100Kbytes的第一页。若系统在RAM找不到它映射的页框(可能交换至磁盘了)。发生缺页异常,并被系统捕获进入缺页异常处理。接下来,系统则会分配一页页框(RAM),找到备份在磁盘的那“页”,并将它换入内存(其实因为换入操作比较昂贵,所以不总是只换入一页,而是预换入多页。这也表明某些文档说:”vmstat某时出现不少si并不能意味着物理内存不足”)。凡是类似这种会迫使进程去睡眠(很可能是由于当前磁盘数据填充至页框(RAM)所花的时间),阻塞当前进程的缺页异常处理称为主缺页(major falut),也称为大缺页(参见下图)。相反,不会阻塞进程的缺页,称为次缺页(minor fault),也称为小缺面。 L3,引用了100Kbytes的第二页。参见第一次访问100Kbytes第一页, Demand Paging。 L4,释放了内存:线性地址区域被删除,页框也被释放。 L5,再次通过*p引用内存页,已被free()了(用户进程本身并不知道)。发生缺页异常,缺面异常处理程序会检查出这个缺页不在进程内存空间之内。对待这种编程错误引起的缺页异常,系统会杀掉这个进程,并且报告著名的段错误(Segmentation fault)。 5. 页框回收SWAP

随着网络并发用户数量增多,进程数量越来越多(比如一般守护进程会fork()子进程来处理用户请求),缺页异常也就更频繁,需要缓存更多的磁盘数据(参考下篇OS Page Cache),RAM也就越来越紧少。为了保证有够用的页框供给缺页异常处理,Linux有一套自己的做法,称为PFRA。PFRA总会从用户态进内存程空间和页面缓存中,“窃取”页框满足供给。所谓”窃取”,指的是:将用户进程内存空间对应占用的页框中的数据swap out至磁盘(称为交换区),或者将OS页面缓存中的内存页(还有用户进程mmap()的内存页)flush(同步fsync())至磁盘设备。

swap out: 由于RAM资源不足,PFRA会将部分匿名页框的数据写入到交换区(swap area)(磁盘),备份之,这个动作称为so(swap out)。

swap in:等到发生内存缺页异常的时候,缺页异常处理程序会将交换区(磁盘)的页面又读回物理内存,这个动作称为si(swap in)。

每次Swapping,都有可能不只是一页数据,不管是si,还是so。Swapping意味着磁盘操作,更新页表等操作,这些操作开销都不小,会阻塞用户态进程。所以,持续飚高的si/so意味着物理内存资源是性能瓶颈。

如果你观察到因为RAM不足导致系统病态式般慢,通常都是因为缺页异常处理,以及PFRA在”盗页”。我们从以下几个方面了解PFRA。

候选页框:找出哪些页框是可以被回收?

进程内存空间占用的页框,比如数据段中的页(Heap, Data),还有在Heap与Stack之间的匿名映射页(比如由malloc()分配的大内存)。但不包括Stack段中的页。 进程空间mmap()的内存页,有映射文件,非匿名映射。 缓存在页面缓存中Buffer/Cache占用的页框。也称OS Page Cache。

页框回收策略:确定了要回收的页框,就要进一步确定先回收哪些候选页框

尽量先回收页面缓存中的Buffer/Cache。其次再回收内存空间占用的页框。 进程空间占用的页框,要是没有被锁定,都可以回收。所以,当某进程睡眠久了,占用的页框会逐渐地交换出去至交换区。 使收LRU置换算法,将那些久而未用的页框优先被回收。这种被放在LRU的unused链表的页,常被认为接下来也不太可能会被引用。 相对回收Buffer/Cache而言,回收进程内存页,昂贵很多。所以,Linux默认只有swap_tendency(交换倾向值)值不小于100时,才会选择换出进程占用的RES。其实交换倾向值描述的是:系统越忙,且RES都被进程占用了,Buffer/Cache只占了一点点的时候,才开始回收进程占用页框。PS:这正表明了,某些DBA提议将MySQL InnoDB服务器vm.swappiness值设置为0,以此让InnoDB Buffer Pool数据在RES呆得更久。 如果实在是没有页框可回收,PFRA使出最狠一招,杀掉一个用户态进程,并释放这些被占的页框。当然,这个被杀的进程不是胡乱选的,至少应该是占用较多页框,运行优选级低,且不是root用户的进程。

激活回收页框:什么时候会回收页框?

紧急回收。系统内核发现没有够用的页框分配,供给读文件和内存缺页处理的时候,系统内核开始”紧急回收页框”。唤醒pdflush内核线程,先将1024页脏页从页面缓存写回磁盘。然后开始回收32页框,若反复回收13次,还收不齐32页框,则发狠杀一个进程。 周期性回收。在紧急回收之前,PFRA还会唤醒内核线程kswapd。为了避免更多的“紧急回收”,当发现空闲页框数量低于设置的警告值时,内核线程kswapd就会被唤醒,回收页框。直到空闲的页框的数量达到设定的安全值。PS:当RES资源紧张的时候,你可以通过ps命令看到更多的kswapd线程被唤醒。 OOM。在高峰时期,RES高度紧张的时候,kswapd持续回收的页框供不应求,直到进入”紧急回收”,直到 OOM。 6. linux内存信息

我们通过free的输出内存内容:

#free total used free shared buffers cached Mem: 16402432 16360492 41940 0 465404 12714880 -/+ buffers/cache: 3180208 13222224 Swap: 8193108 264 8192844

Gnomic智能体平台
Gnomic智能体平台

国内首家无需魔法免费无限制使用的ChatGPT4.0,网站内设置了大量智能体供大家免费使用,还有五款语言大模型供大家免费使用~

下载

total:物理内存的总大小。used:已经使用的物理内存多小。free:空闲的物理内存值。shared:多个进程共享的内存值。buffers/cached:磁盘缓存的大小。第三行(-/+ buffers/cached):代表磁盘缓存使用状态。第四行:Swap表示交换空间内存使用状态。公式:-buffers/cache 的内存数:used - buffers - cached+buffers/cache 的内存数:free + buffers + cached可用的memory=free memory+buffers+cachedfree命令输出的内存状态,可以通过两个角度来查看:一个是从内核的角度来看,一个是从应用层的角度来看的。

1.从内核的角度来查看内存的状态:(对于OS,buffers/cached 都是属于被使用) 内核目前可以直接分配到,不需要额外的操作,即为上面free命令输出中第二行Mem项的值,可以看出,此系统物理内存有4G,空闲的内存只有41940K,也就是40M多一点,我们来做一个这样的计算: 16402432-16360492=41940 就是总的物理内存减去已经使用的物理内存得到的就是空闲的物理内存大小。 注意:这里的可用内存值41940并不包含处于buffers和cached状态的内存大小。 注意:实际上,内核完全控制着内存的使用情况,linux会在需要内存的时候,或在系统运行逐步推进时,将buffers和cached状态的内存变为free状态的内存,以供系统使用。

2.从应用层的角度来看系统内存的使用状态 也就是linux上运行的应用程序可以使用的内存大小,即free命令第三行“(-/+ buffers/cached)”的输出,可以看到,此系统已经使用的内存才3180208K,而空闲的内存达到13222224K,继续做这样一个计算: 41940(Men:free)+(465404(Men:buffers)+12714880(Men:cached))=13222224(-/+buffers/cached:free) 通过这个等式可知,应用程序可用的物理内存值是Mem项的free值加上buffers和cached值之和,也就是说,这个free值是包括buffers和cached项大小的。 对于应用程序来说,buffers/cached占有的内存是可用的,因为buffers/cached是为了提高文件读取的性能,当应用程序需要用到内存的时候buffers/cached会很快地被回收,以供应用程序使用

7. buffer和cache是什么
1)、计算机领域的buffer和cache

buffer和cache是两个在计算机技术中被用滥的名词,放在不通语境下会有不同的意义。

Cache:高速缓存,是位于CPU与主内存间的一种容量较小但速度很高的存储器。由于CPU的速度远高于主内存,CPU直接从内存中存取数据要等待一定时间周期,Cache中保存着CPU刚用过或循环使用的一部分数据,当CPU再次使用该部分数据时可从Cache中直接调用这样就减少了CPU的等待时间提高了系统的效率。Cache又分为一级Cache(L1 Cache)和二级Cache(L2 Cache),L1 Cache集成在CPU内部,L2 Cache早期一般是焊在主板上现在也都集成在CPU内部,常见的容量有256KB或512KB L2 Cache。

Buffer:缓冲区,一个用于存储速度不同步的设备或优先级不同的设备之间传输数据的区域。通过缓冲区,可以使进程之间的相互等待变少,从而使从速度慢的设备读入数据时,速度快的设备的操作进程不发生间断。

cache最初用于cpu cache, 主要原因是cpu 与memory, 由于cpu快,memory跟不上,且有些值使用次数多,所以放入cache中,主要目的是,重复使用, 并且一级\二级物理cache速度快,buffer主要用于disk与 memory,主要是保护硬盘或减少网络传输的次数(内存数据表现dataSet).当然也可以提高速度(不会立即写入硬盘或直接从硬盘中读出的数据马上显示),重复使用,最初最主要的目的是保护disk。

2)、linux Free的buffer和cache

linuxFree中的buffer和cache:(它们都是占用内存):

在Linux操作系统中,当应用程序需要读取文件中的数据时,操作系统先分配一些内存,将数据从磁盘读入到这些内存中,然后

再将数据分发给应用程序;当需要往文件中写数据时,操作系统先分配内存接收用户数据,然后再将数据从内存写到磁盘上。然而,如果有大量数据需要从磁盘读取到内存或者由内存写入磁盘时,系统的读写性能就变得非常低下,因为无论是从磁盘读数据,还是写数据到磁盘,都是一个很消耗时间和资源的过程,在这种情况下,为了提高磁盘存取效率, Linux做了一些精心的设计, 除了对dentry进行缓存(用于VFS,加速文件路径名到inode的转换), 还采取了两种主要Cache方式:Buffer Cache和Page Cache。前者针对磁盘块的读写,后者针对文件inode的读写。这些Cache有效缩短了 I/O系统调用(比如read,write,getdents)的时间。"

buffers与cached都是内存操作,用来保存系统曾经打开过的文件以及文件属性信息,这样当操作系统需要读取某些文件时,会首先在buffers与cached内存区查找,如果找到,直接读出传送给应用程序,如果没有找到需要数据,才从磁盘读取,这就是操作系统的缓存机制,通过缓存,大大提高了操作系统的性能。但buffers与cached缓冲的内容却是不同的。

buffers:作为buffer cache的内存,是用来缓冲块设备做的,对块的操作会使用buffer cache进行缓存,它只记录文件系统的元数据(metadata)以及 tracking in-flight pages。比如我们在格式化文件系统的时候,查找系统文件等。

cached:作为page cache的内存,是用来给文件做缓冲。如果 cache 的值很大,说明cache住的文件数很多。如果频繁访问到的文件都能被cache住,那么磁盘的读IO bi会非常小。

更通俗一点说:buffers主要用来存放目录里面有什么内容\文件的属性\权限等等。cached直接用来记忆我们打开过的文件和程序。

一般情况下两个缓存系统是一起配合使用的:

磁盘的操作有逻辑级(文件系统)和物理级(磁盘块),这两种Cache就是分别缓存逻辑和物理级数据的。Page cache实际上是针对文件系统的,是文件的缓存,在文件层面上的数据会缓存到page cache。文件的逻辑层需要映射到实际的物理磁盘,这种映射关系由文件系统来完成。当page cache的数据需要刷新时,page cache中的数据交给buffer cache,因为Buffer Cache就是缓存磁盘块的。但是这种处理在2.6版本的内核之后就变的很简单了,没有真正意义上的cache操作。Buffer cache是针对磁盘块的缓存,也就是在没有文件系统的情况下,直接对磁盘进行操作的数据会缓存到buffer cache中,例如,文件系统的元数据都会缓存到buffer cache中。简单说来,page cache用来缓存文件数据,buffer cache用来缓存磁盘数据。在有文件系统的情况下,对文件操作,那么数据会缓存到page cache,如果直接采用dd等工具对磁盘进行读写,那么数据会缓存到buffer cache。

比如当我们对一个文件进行写操作的时候,page cache的内容会被改变,而buffer cache则可以用来将page标记为不同的缓冲区,并记录是哪一个缓冲区被修改了。

这样,内核在后续执行脏数据的回写(writeback)时,就不用将整个page写回,而只需要写回修改的部分即可。

为了验证我们的结论是否正确。

buffers验证:

我们在执行find /* -name *.conf之前的free数值:

[root@localhost4 ~]# free -m total used free shared buffers cached Mem: 3740 649 3090 0 74 123 -/+ buffers/cache: 450 3289 Swap: 3871 0 3871

find /* -name *.conf

看看buffers的值是否变化:

total used free shared buffers cached Mem: 3740 1183 2556 0 486 123 -/+ buffers/cache: 573 3166 Swap: 3871 0 3871

buffers为486MB, cached不变 ,buffers变大啦。

然后重复执行find命令,看看两次显示速度有何不同。

cached验证:

cached:可以通过vi打开一个非常大的文件,看看cached的变化,然后再次vi这个文件,感觉一下两次打开的速度有何异同,是不是第二次打开的速度明显快于第一次呢?此外我们通过复制文件:

[root@localhost4 ~]# cp -r /etc ~/test/ [root@localhost4 ~]# free -m total used free shared buffers cached Mem: 3740 1261 2479 0 487 194 -/+ buffers/cache: 579 3160 Swap: 3871 0 3871

都cached已经变大啦。

Linux操作系统的内存运行原理,很大程度上是根据服务器的需求来设计的,例如系统的缓冲机制会把经常使用到的文件和数据缓存在cached中,linux总是在力求缓存更多的数据和信息,这样再次需要这些数据时可以直接从内存中取,而不需要有一个漫长的磁盘操作,这种设计思路提高了系统的整体性能。

3)、如何回收cached

Linux内核会在内存将要耗尽的时候,触发内存回收的工作,以便释放出内存给急需内存的进程使用。 一般情况下,这个操作中主要的内存释放都来自于对buffer/cache的释放。尤其是被使用更多的cache空间。既然它主要用来做缓存,只是在内存够用的时候加快进程对文件的读写速度,那么在内存压力较大的情况下,当然有必要清空释放cache,作为free空间分给相关进程使用。 所以一般情况下,我们认为buffer/cache空间可以被释放,这个理解是正确的。 但是这种清缓存的工作也并不是没有成本。理解cache是干什么的就可以明白清缓存必须保证cache中的数据跟对应文件中的数据一致,才能对cache进行释放。 所以伴随着cache清除的行为的,一般都是系统IO飙高。因为内核要对比cache中的数据和对应硬盘文件上的数据是否一致,如果不一致需要写回,之后才能回收。 在系统中除了内存将被耗尽的时候可以清缓存以外,我们还可以使用下面这个文件来人工触发缓存清除的操作:

/proc是一个虚拟文件系统,我们可以通过对它的读写操作做为与kernel实体间进行通信的一种手段.也就是说可以通过修改/proc中的文件,来对当前kernel的行为做出调整.那么我们可以通过调整/proc/sys/vm/drop_caches来释放内存.操作如下:

[root@localhost4 ~]# cat /proc/sys/vm/drop_caches 0

首先,/proc/sys/vm/drop_caches的值,默认为0

这个文件可以设置的值分别为1、2、3。它们所表示的含义为:

echo 1 > /proc/sys/vm/drop_caches:表示清除page cache。

echo 2 > /proc/sys/vm/drop_caches:表示清除回收slab分配器中的对象(包括目录项缓存和inode缓存)。slab分配器是内核中管理内存的一种机制,其中很多缓存数据实现都是用的pagecache。

echo 3 > /proc/sys/vm/drop_caches:表示清除page cache和slab分配器中的缓存对象。

[root@localhost4 ~]# echo 3 > /proc/sys/vm/drop_caches [root@localhost4 ~]# free -m total used free shared buffers cached Mem: 3740 465 3274 0 0 29 -/+ buffers/cache: 435 3304 Swap: 3871 0 3871

再来运行free命令,buffers为0MB,cached为29MB.那么有效的释放了buffer和cache.

8. Linux SWAP使用情况

如果系统的物理内存用光了,则会用到swap。系统就会跑得很慢,但仍能运行;如果Swap空间用光了,那么系统就会发生错误。通常会出现“application is out of memory”的错误,严重时会造成服务进程的死锁。所以要高度重视。如果常常swap用很多,可能你就要考虑加物理内存了.这也是linux看内存是否够用的标准。

我们通过free,top,cat /proc/swaps、swapon -s 等命令查看Swap分区的情况.

通过vmstat -n 1也能查看到当前系统使用内存的变化:

[root@localhost4 ~]# vmstat -n 1 procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 0 0 0 3350920 3632 30724 0 0 20 8 27 51 0 0 99 0 0 0 0 0 3350912 3632 30776 0 0 0 0 96 166 0 0 100 0 0

Memory(内存): swpd: 使用虚拟内存大小 free: 可用内存大小 buff: 用作缓冲的内存大小 cache: 用作缓存的内存大小

Swap: si (swap in ): 每秒从交换区写到内存的大小 so(swap out): 每秒写入交换区的内存大小

在Linux内核 2.6.16中引入了一个系统内存接口特性,这个接口位于/proc/$pid/目录下的smaps文件中 ,一看内容发现是进程内存映像信息,比同一目录下的maps文件更详细些。 cat /proc/1/smaps

ps -ef|grep nginx root 2164 2163 0 10:48 ? 00:00:00 nginx: master process www 2165 2164 0 10:48 ? 00:00:00 nginx: worker process cat /proc/2164/smaps ... 7fffa8962000-7fffa8977000 rw-p 00000000 00:00 0 [stack] Size: 88 kB Rss: 20 kB Pss: 12 kB Shared_Clean: 0 kB Shared_Dirty: 8 kB Private_Clean: 0 kB Private_Dirty: 12 kB Referenced: 20 kB Anonymous: 20 kB AnonHugePages: 0 kB Swap: 0 kB KernelPageSize: 4 kB MMUPageSize: 4 kB

解释下samps里面的内容:

7fffa8962000-7fffa8977000 是该虚拟内存段的开始和结束位置 rw-p 内存段的权限,rw是指可读写,p是指私有,如果是s则为共享00000000该虚拟内存段在对应的映射文件中的偏移量00:00 文件的主设备和次设备号0被映射到虚拟内存的文件的索引节点号 [stack] 被映射到虚拟内存的文件名称 Size 是进程使用内存空间,并不一定实际分配了内存(VSS) Rss是实际分配的内存(不需要缺页中断就可以使用的) Shared_Clean 和其他进程共享的未改写页面 Shared_Dirty 和其他进程共享的已改写页面 Private_Clean 未改写的私有页面页面 Private_Dirty 已改写的私有页面页面 Swap 存在于交换分区的数据大小(如果物理内存有限,可能存在一部分在主存一部分在交换分区) Pss是平摊计算后的使用内存(有些内存会和其他进程共享,例如mmap进来的)

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
mysql修改数据表名
mysql修改数据表名

MySQL修改数据表:1、首先查看数据库中所有的表,代码为:‘SHOW TABLES;’;2、修改表名,代码为:‘ALTER TABLE 旧表名 RENAME [TO] 新表名;’。php中文网还提供MySQL的相关下载、相关课程等内容,供大家免费下载使用。

686

2023.06.20

MySQL创建存储过程
MySQL创建存储过程

存储程序可以分为存储过程和函数,MySQL中创建存储过程和函数使用的语句分别为CREATE PROCEDURE和CREATE FUNCTION。使用CALL语句调用存储过程智能用输出变量返回值。函数可以从语句外调用(通过引用函数名),也能返回标量值。存储过程也可以调用其他存储过程。php中文网还提供MySQL创建存储过程的相关下载、相关课程等内容,供大家免费下载使用。

513

2023.06.21

mongodb和mysql的区别
mongodb和mysql的区别

mongodb和mysql的区别:1、数据模型;2、查询语言;3、扩展性和性能;4、可靠性。本专题为大家提供mongodb和mysql的区别的相关的文章、下载、课程内容,供大家免费下载体验。

287

2023.07.18

mysql密码忘了怎么查看
mysql密码忘了怎么查看

MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS 应用软件之一。那么mysql密码忘了怎么办呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

519

2023.07.19

mysql创建数据库
mysql创建数据库

MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS 应用软件之一。那么mysql怎么创建数据库呢?php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

267

2023.07.25

mysql默认事务隔离级别
mysql默认事务隔离级别

MySQL是一种广泛使用的关系型数据库管理系统,它支持事务处理。事务是一组数据库操作,它们作为一个逻辑单元被一起执行。为了保证事务的一致性和隔离性,MySQL提供了不同的事务隔离级别。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

392

2023.08.08

sqlserver和mysql区别
sqlserver和mysql区别

SQL Server和MySQL是两种广泛使用的关系型数据库管理系统。它们具有相似的功能和用途,但在某些方面存在一些显著的区别。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

542

2023.08.11

mysql忘记密码
mysql忘记密码

MySQL是一种关系型数据库管理系统,关系数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性。那么忘记mysql密码我们该怎么解决呢?php中文网给大家带来了相关的教程以及其他关于mysql的文章,欢迎大家前来学习阅读。

668

2023.08.14

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

76

2026.03.11

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
进程与SOCKET
进程与SOCKET

共6课时 | 0.4万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号