@@ -621,8 +621,96 @@ CPU 使用率描述了非空闲时间占总 CPU 时间的百分比,根据 CPU
621
621
622
622
# # 内存
623
623
624
+ 同 CPU 管理一样,内存管理也是操作系统最核心的功能之一。内存主要用来存储系统和应用程序的指令、数据、缓存等。
625
+
624
626
# ## 内存原理
625
- # ### 地址空间
627
+ # ### 内存映射
628
+ 我们通常所说的内存容量,其实指的是物理内存。物理内存也称为主存,大多数计算机用的主存都是动态随机访问内存(DRAM)。只有内核才可以直接访问物理内存。那么,进程要访问内存时,该怎么办呢?
629
+ Linux 内核给每个进程都提供了一个独立的虚拟地址空间,并且这个地址空间是连续的。这样,进程就可以很方便地访问内存,更确切地说是访问虚拟内存。
630
+
10000
虚拟地址空间的内部又被分为内核空间和用户空间两部分,不同字长(也就是单个 CPU 指令可以处理数据的最大长度)的处理器,地址空间的范围也不同。
631
+ ! [[Pasted image 20250625194349.png]]
632
+
633
+ 通过这里可以看出,32 位系统的内核空间占用 1G,位于最高处,剩下的 3G 是用户空间。而 64 位系统的内核空间和用户空间都是 128T,分别占据整个内存空间的最高和最低处,剩下的中间部分是未定义的。
634
+
635
+ 还记得进程的用户态和内核态吗?进程在用户态时,只能访问用户空间内存;只有进入内核态后,才可以访问内核空间内存。虽然每个进程的地址空间都包含了内核空间,但这些内核空间,其实关联的都是相同的物理内存。这样,进程切换到内核态后,就可以很方便地访问内核空间内存。
636
+
637
+ 既然每个进程都有一个这么大的地址空间,那么所有进程的虚拟内存加起来,自然要比实际的物理内存大得多。所以,并不是所有的虚拟内存都会分配物理内存,只有那些实际使用的虚拟内存才分配物理内存,并且分配后的物理内存,是通过内存映射来管理的。
638
+ 内存映射,其实就是将虚拟内存地址映射到物理内存地址。为了完成内存映射,内核为每个进程都维护了一张页表,记录虚拟地址与物理地址的映射关系,如下图所示:
639
+
640
+ ! [[Pasted image 20250625194526.png]]
641
+
642
+ 页表实际上存储在 CPU 的内存管理单元 MMU 中,这样,正常情况下,处理器就可以直接通过硬件,找出要访问的内存
643
+ 而当进程访问的虚拟地址在页表中查不到时,系统会产生一个缺页异常,进入内核空间分配物理内存、更新进程页表,最后再返回用户空间,恢复进程的运行。
644
+ TLB(Translation Lookaside Buffer,转译后备缓冲器)会影响 CPU 的内存访问性能,在这里其实就可以得到解释。
645
+ TLB 其实就是 MMU 中页表的高速缓存。由于进程的虚拟地址空间是独立的,而 TLB 的访问速度又比 MMU 快得多,所以,通过减少进程的上下文切换,减少 TLB 的刷新次数,就可以提高 TLB 缓存的使用率,进而提高 CPU 的内存访问性能。
646
+ 不过要注意,MMU 并不以字节为单位来管理内存,而是规定了一个内存映射的最小单位,也就是页,通常是 4 KB 大小。这样,每一次内存映射,都需要关联 4 KB 或者 4KB 整数倍的内存空间。
647
+
648
+ 页的大小只有 4 KB ,导致的另一个问题就是,整个页表会变得非常大。比方说,仅 32 位系统就需要 100 多万个页表项(4GB/4KB),才可以实现整个地址空间的映射。为了解决页表项过多的问题,Linux 提供了两种机制,也就是多级页表和大页(HugePage)。
649
+
650
+ 多级页表就是把内存分成区块来管理,将原来的映射关系改成区块索引和区块内的偏移。由于虚拟内存空间通常只用了很少一部分,那么,多级页表就只保存这些使用中的区块,这样就可以大大地减少页表的项数。
651
+
652
+ Linux 用的正是四级页表来管理内存页,如下图所示,虚拟地址被分为 5 个部分,前 4 个表项用于选择页,而最后一个索引表示页内偏移。
653
+ ! [[Pasted image 20250625194918.png]]
654
+
655
+ 再看大页,顾名思义,就是比普通页更大的内存块,常见的大小有 2MB 和 1GB。大页通常用在使用大量内存的进程上,比如 Oracle、DPDK 等。
656
+ 通过这些机制,在页表的映射下,进程就可以通过虚拟地址来访问物理内存了。那么具体到一个 Linux 进程中,
657
+
658
+
659
+ # ### 虚拟内存地址空间分布
660
+
661
+ ! [[Pasted image 20250625195050.png]]
662
+
663
+ # ### 查看内存使用情况
664
+
665
+ # #### free
666
+ ` ` ` bash
667
+ # 注意不同版本的free输出可能有差异
668
+ [root@localhost ~ ]# free
669
+ total used free shared buff/cache available
670
+ Mem: 32316380 3015856 25904268 639424 4503344 29300524
671
+ Swap: 16351228 0 16351228
672
+ ` ` `
673
+
674
+ - 第一列,total 是总内存大小;
675
+ - 第二列,used 是已使用内存的大小,包含了共享内存;第三列,free 是未使用内存的大小;
676
+ - 第四列,shared 是共享内存的大小;
677
+ - 第五列,buff/cache 是缓存和缓冲区的大小;
678
+ - 最后一列,available 是新进程可用内存的大小。
679
+
680
+ free查看的是系统内存使用情况,如果想查看具体进程内存使用情况,需要使用top或者ps查看
681
+
682
+ ` ` ` bash
683
+ # 按下 M 切换到按照内存排序
684
+ top - 19:57:37 up 7 days, 2:59, 3 users, load average: 0.08, 0.03, 0.00
685
+ Tasks: 347 total, 1 running, 346 sleeping, 0 stopped, 0 zombie
686
+ %Cpu(s): 0.0 us, 0.0 sy, 0.0 ni, 99.9 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
687
+ MiB Mem : 9.3/31559.0 [|||||||| | ]
688
+ MiB Swap: 0.0/15968.0 [ ]
689
+ PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
690
+ 1 root 20 0 172692 16260 9600 S 0.0 0.1 3:25.35 systemd
691
+ 2 root 20 0 0 0 0 S 0.0 0.0 0:00.80 kthreadd
692
+ 3 root 20 0 0 0 0 S 0.0 0.0 0:00.00 pool_workqueue_
693
+ 4 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 kworker/R-rcu_g
694
+ 5 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 kworker/R-rcu_p
695
+ 6 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 kworker/R-slub_
696
+ 7 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 kworker/R-netns
697
+ 9 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 kworker/0:0H-events_highpri
698
+ 11 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 kworker/R-mm_pe
699
+ 13 root 20 0 0 0 0 I 0.0 0.0 0:00.00 rcu_tasks_kthre
700
+ ` ` `
701
+ - VIRT 是进程虚拟内存的大小,只要是进程申请过的内存,即便还没有真正分配物理内存,也会计算在内。
702
+ - RES 是常驻内存的大小,也就是进程实际使用的物理内存大小,但不包括 Swap 和共享内存。
703
+ - SHR 是共享内存的大小,比如与其他进程共同使用的共享内存、加载的动态链接库以及程序的代码段等。
704
+ - %MEM 是进程使用物理内存占系统总内存的百分比。
705
+
706
+ 在查看 top 输出时,你还要注意两点。
707
+ - 第一,虚拟内存通常并不会全部分配物理内存。从上面的输出,你可以发现每个进程的虚拟内存都比常驻内存大得多。
708
+ - 第二,共享内存 SHR 并不一定是共享的,比方说,程序的代码段、非共享的动态链接库,也都算在 SHR 里。当然,SHR 也包括了进程间真正共享的内存。所以在计算多个进程的内存使用时,不要把所有进程的 SHR 直接相加得出结果
709
+
710
+
711
+ # ### 怎么理解内存中的Buffer和Cache?
712
+
713
+
626
714
# ### 虚拟内存
627
715
# ### 内存分配与回收
628
716
# ### 缓存与缓冲区
@@ -635,7 +723,7 @@ CPU 使用率描述了非空闲时间占总 CPU 时间的百分比,根据 CPU
635
723
# ### SWAP使用量
636
724
637
725
# ## 性能剖析
638
- # ### free
726
+
639
727
# ### top
640
728
# ### sar
641
729
# ### vmstat
0 commit comments