第一次写关于内存的文章。网上很多信息都是极度碎片化,所以单独写一个文章整理一下,融入一些当前的技术沉淀汇总。如果有不同想法可以评论区评论,我都会回复的。
CPU
CPU只能一个总线周期最多存取一次总线。
ARM是一系列基于精简指令集计算机(RISC)架构的中央处理单元(CPU),全称为“Advanced RISC Machine”(高级精简指令集机器)。
定义:ARM处理器是一种高性能、低功耗的微处理器,由英国ARM公司开发并授权给各大厂商使用。它采用精简指令集(RISC)设计,旨在通过简化指令集来提高执行效率和降低功耗。
ARM处理器在电子设备中扮演着核心计算和控制的角色,负责执行软件指令、处理数据和协调设备的各个部件协同工作。
ARM处理器因其低功耗、高性能和低成本的特点,被广泛应用于各种电子设备中。
DMA外设联通
DMA的英文拼写是“Direct Memory Access”,汉语的意思就是直接内存访问。
嵌入式系统的DMA控制器内建在 处理器芯片内部,一般称为DMA控制器,DMAC。
DMA 控制逻辑 由CPU 和 DMA控制接口逻辑芯片 共同组成。
一个处理器可以包含多个DMA控制器。每个控制器有多个DMA通道,以及多条直接与存储器站(memory bank)和外设连接的总线。
外设始终速率低,内部存储时钟速率高。
通常只有数据流量较大的外设,才需要支持DMA能力。例如:视频、音频、网络接口
DMA的好处就是它不需要CPU的干预而直接服务外设。DMA通过一组专用总线将内部和外部存储器与具有DMA能力的外设连接起来。DMA能一次传输几个甚至几十个字节的数据,所以使用DMA能使设备的吞吐能力大为增强。
DMA使用物理地址,程序是使用虚拟地址的,所以配置DMA时必须将虚拟地址转化成物理地址。
因为程序使用虚拟地址,而且一般使用cache地址,所以Cache中的内容与其物理地址(内存)的内容不一定一致,所以在启动DMA传输前一定要将该地址的cache刷新,即写入内存。
OS并不能保证每次分配到的内存空间在物理上是连续的。尤其是在系统使用过一段时间而又分配了一块比较大的内存时。所以每次都需要判断地址是不是连续的,如果不连续就需要把这段内存分成几段让DMA完成传输
| 地址寄存器 | 存放DMA传输时存储单元地址 |
| 字节计数器 | 存放DMA传输的字节数 |
| 控制寄存器 | 存放由CPU设定的DMA传输方式,控制命令等 |
| 状态寄存器 | 存放DMAC当前的状态,包括有无DMA请求,是否结束等 |
一般是由一条数据总线、地址总线和控制寄存器组成。能在控制器内部计算出地址。
每个DMA控制器有一组FIFO,起到DMA子系统和外设或存储器之间的缓冲器的作用。
UART 协议
缺点:传输速率相对较低、只能实现点对点通信等。在高速、多设备的通信环境下,可能需要使用其他更高级的通信协议。
UART (Universal Asynchronous Receiver/Transmitter,通用异步收发器) 是一种常用的串行通信协议,用于在计算机和外部设备之间传输数据。它是一种异步通信协议,也就是说数据的传输不需要事先建立好同步时钟信号。
UART 协议定义了数据传输的格式和通信规则。它包括一系列的控制信号和数据线来实现双向的串行通信。常见的 UART 协议有 RS-232、RS-422 和 RS-485 等。
UART 协议的工作方式是通过不断发送和接收数据来实现通信。发送方将数据按照一定的格式组织成数据帧,并通过数据线发送出去。接收方接收到数据后,解析数据帧,并进行相应的处理。
L1存储器
位置:最接近CPU核心,分为L1指令缓存和L1数据缓存。
作用:作为CPU访问最频繁的数据和指令的临时存储区域,其读写速度极快,能够极大地减少CPU访问主存的次数,提高系统性能。
与DMA关联:在DMA传输过程中,如果目标数据或指令频繁被CPU访问,DMA可能会将这些数据或指令预加载到L1缓存中,以减少CPU的等待时间。
L2存储器
位置:位于L1缓存和主存之间,比L1缓存更远但比主存更近。
作用:作为L1缓存和主存之间的桥梁,当L1缓存未命中时,CPU会访问L2缓存以寻找所需数据。L2缓存的容量通常比L1大,但速度稍慢。
与DMA关联:DMA在传输大量数据时,可能会利用L2缓存作为数据的中转站,尤其是在L1缓存已满或无法容纳所有数据时。
L3存储器
位置:距离CPU核心最远,是CPU缓存体系中的最后一级。
作用:作为所有CPU内核共享的缓存区域,其容量最大但速度相对较慢。L3缓存用于存储那些被多个CPU内核频繁访问的数据,以减少对主存的访问次数。
与DMA关联:在需要传输大量数据到多个CPU内核或需要优化整体系统性能的场景中,DMA可能会将部分数据直接传输到L3缓存中,以便多个CPU内核能够共享这些数据。
| 存储器级别 | 位置 | 容量 | 速度 | 访问方式 |
|---|---|---|---|---|
| L1存储器 | 最接近CPU核心 | 几十KB到几百KB | 最快(2~4个CPU时钟周期) | 每个CPU内核独立访问 |
| L2存储器 | 位于L1和主存之间 | 几百KB到几MB(每个内核) | 次快(10~20个CPU时钟周期) | 每个CPU内核独立访问 |
| L3存储器 | 距离CPU核心最远 | 几MB到几十MB(共享) | 最慢(20~60个CPU时钟周期) | 所有CPU内核共享访问 |
目前在计算机中,主要有两大存储器 SRAM 和 DRAM。
主存储器是由 DRAM 实现的,也就是我们常说的内存,在 CPU 里通常会有 L1、L2、L3 这样三层高速缓存是用 SRAM 实现的。
SRAM 被称为“静态”存储器,是因为只要处在通电状态,里面的数据就可以保持存在。而一旦断电,里面的数据就会丢失了。
目前 SRAM 主要集成在 CPU 里面,每个 CPU 核心都有一块属于自己的 L1 高速缓存,通常分成指令缓存和数据缓存,分开存放 CPU 使用的指令和数据。L2 的 Cache 同样是每个 CPU 核心都有的,不过它往往不在 CPU 核心的内部。所以,L2 cache 的访问速度会比 L1 稍微慢一些。而 L3 cache ,则通常是多个 CPU 核心共用的。
是否在核心内部,涉及到了访问其是否会发生中断,而这个中断一般是由DMA实现的中断,DMA拥有相对独立的中断机制,去使用内核总线进行数据交互。一般用于L1cache
系统总线一般是用于L2cache
写直达
将数据写入 cache 之后同时将这个数据立马写入到主存中,但是由于主存和 cache 本身性能差异,那么每次在写入主存的时候都将花费大量的时间。解决办法就是加一层写缓冲(write buffer),这样 CPU 在将数据写入 cache 和缓冲之后可以继续执行,等到缓冲写入到主存中再释放。
写回
但是如果写入速度大于缓冲释放速度,那么还是会阻塞 CPU 执行。那么可以考虑一下写回策略,这种机制会在每次写入的时候仅将新值写入 cache 中,只有当修改过的块被替换时才需要写到较低层存储结构中。
一致性与 MESI 协议
由于现在都是多核 CPU,并且 cache 分了多级,并且数据存在共享的情况,所以需要一种机制保证在不同的核中看到的 cache 数据必须时一致的。最常用来处理多核 CPU 之间的缓存一致性协议就是 MESI 协议。
MESI 协议,是一种叫作写失效(Write Invalidate)的协议。
在写失效协议里,只有一个 CPU 核心负责写入数据,其他的核心,只是同步读取到这个写入。
在这个 CPU 核心写入 cache 之后,它会去广播一个“失效”请求告诉所有其他的 CPU 核心。
MESI 协议对应的四个不同的标记,分别是:
• M:代表已修改(Modified)
• E:代表独占(Exclusive)
• S:代表共享(Shared)
• I:代表已失效(Invalidated)
在独占状态下,对应的 cache Line 只加载到了当前 CPU 核所拥有的 cache 里。其他的 CPU 核,并没有加载对应的数据到自己的 cache 里。这个时候,如果要向独占的 cache Block 写入数据,我们可以自由地写入数据,而不需要告知其他 CPU 核。
共享状态就是在多核中同时加载了同一份数据。所以在共享状态下想要修改数据要先向所有的其他 CPU 核心广播一个请求,要求先把其他 CPU 核心里面的 cache ,都变成无效的状态,然后再更新当前 cache 里面的数据。
虚拟内存映射
在我们日常使用的 Linux 或者 Windows 操作系统下,程序并不能直接访问物理内存。程序都是通过虚拟地址 VA(virtual address)用地址转换翻译成 PA 物理地址(physical address)才能获取到数据。也就是说 CPU 操作的实际上是一个虚拟地址 VA。
CPU 访问主存的时候会将一个虚拟地址(virtual address)被内存管理单元(Memory Management Unint, MMU)进行翻译成物理地址 PA(physical address) 才能访问。
把虚拟内存地址,映射到物理内存地址,最直观的办法,就是来建一张映射表。这个映射表在计算机中叫页表(Page Table)。
[虚拟页号] 根据 [页表] 找到 [物理页],然后 [偏移] 找到物理准确位置。
内存换页策略
提高内存替换的效率和准确性。
支持新的页面替换策略。这需要对Linux内存管理子系统有深入的理解,包括mm/vmscan.c等关键文件。
可以通过调整内核参数(如vm.swappiness, vm.dirty_ratio等)来间接影响内存管理的行为。
numactl
调整vm.dirty_ratio参数需要权衡内存占用和磁盘I/O性能。在内存资源紧张的情况下,降低dirty_ratio值可以避免内存溢出;而在磁盘I/O性能较好且内存资源充裕的情况下,可以适当增加dirty_ratio值以提高系统性能。
除了vm.dirty_ratio外,还有其他与脏页处理相关的参数(如vm.dirty_background_ratio、vm.dirty_expire_centisecs等),它们共同决定了系统的脏页处理策略。因此,在调整这些参数时,需要综合考虑它们之间的相互影响。
vm.swappiness参数控制系统在内存不足时,是否使用交换空间(swap space)以及使用的相对权重。简而言之,它决定了系统何时开始将内存中的数据页交换到磁盘上的交换空间中,以释放物理内存给其他进程使用。【本人私有文档 悟 中有详细记录】
vm.dirty_ratio参数表示系统内存的一部分用于写回脏页缓冲区(dirty page cache)的最大比例。脏页是指那些已经被修改但尚未写回磁盘的内存页。这个参数控制系统在内存中的脏页数量达到什么比例时,应该开始将脏页写回磁盘。
注:MMU做虚拟地址转物理地址
SWAP
-
通常情况:当系统内存再次变得充足时,Linux会尝试将swap分区中的数据迁移回内存。这意味着swap分区中的数据并非被永久占用,而是一个临时性的缓冲区。系统会根据需要自动管理swap分区中的数据,以确保系统的性能和稳定性。
-
手动干预
特殊情况:在某些特定情况下,也可以手动干预swap分区中数据的释放。例如,通过swapoff命令可以关闭swap分区,这将导致swap分区中的数据全部被丢弃(因为swap分区在关闭时不会将数据写回内存或其他存储设备)。然后,可以通过swapon命令重新打开swap分区,但此时swap分区是空的,因为它之前的数据已经被丢弃。
然而,需要注意的是,手动关闭swap分区可能会影响系统的性能和稳定性,因为这会减少系统可用的内存资源。因此,在进行此类操作之前,应该仔细评估其对系统的影响。
-
交换过程
换入(swap in)和换出(swap out):在Linux中,内存管理是一个动态的过程。当某个内存页(page)不再被频繁访问时,它可能会被标记为不常用的,并随后被写入swap分区(称为换出)。而当这个内存页再次被需要时,系统会从swap分区中读取它回内存(称为换入)。这个过程是自动的,由内核的内存管理子系统控制。 -
注意事项
在进行系统管理时,应该定期监控swap分区的使用情况,以确保系统不会因为swap分区满而导致性能下降或崩溃。
如果发现swap分区频繁使用,并且系统性能受到影响,可能需要考虑增加物理内存或优化应用程序的内存使用。
NUMA
Numa(Non-Uniform Memory Access)是一种计算机架构,它允许多个处理器通过共享内存来访问系统的物理内存,但是不同的内存区域可能由不同的处理器处理,这种内存访问方式被称为“非一致性内存访问”。
在这种架构中,每个处理器都有自己的本地内存,但它们可以访问其他处理器的内存。这种架构可以提高多处理器系统的性能,并使系统更具可扩展性。
如果系统中没有安装 numactl 工具,需要先安装该工具。在大多数 Linux 发行版中,可以使用以下命令进行安装:
sudo apt-get install numactl # Ubuntu/Debian
sudo yum install numactl # CentOS/Fedora
numactl --hardware
numastat -c
以下是每个统计量的含义:
Numa_Hit:表示进程成功从该节点的本地内存中获取的内存总量(单位为MB)。
Numa_Miss:表示进程无法从该节点的本地内存中获取所需内存的总量(单位为MB)。
Numa_Foreign:表示由于数据已从本地节点迁移而进程在远程 NUMA 节点上访问的内存总量(单位为MB)。
Interleave_Hit:表示进程从多个节点交错分配的内存中获取的总量(单位为MB)。
Local_Node:表示进程在每个节点的本地内存中使用的总量(单位为MB)。
Other_Node:表示在该节点上分配了内存,但无法确定该内存被哪个节点使用,这可能是由于内存映射不完全或跨节点的共享内存导致的。
Node 0:表示第一个NUMA节点的总内存使用情况(单位为MB)。
Node 1:表示第二个NUMA节点的总内存使用情况(单位为MB)。
Total:表示所有节点的总内存使用情况(单位为MB)。
numastat -c 命令的输出表示的是自系统启动以来的历史内存使用情况
看每个NUMA节点的内存使用情况:
numastat -m
查看NUMA下指定进程的运行情况
numastat -p <pid>
创建新进程时,指定NUMA的相关属性
numactl --membind=0 --cpunodebind=0 ./myprog
新进程在所有节点上运行
numactl --interleave=all ./myprog
注:进程绑定到所有的CPU和内存上可能会影响性能,因为在NUMA节点之间交错分配内存可能会导致额外的内存访问延迟。
附录
在open stack环境中如果创建ECS失败是由numa导致可以修改该虚拟机规格中的numa标签进行修复,例如以下实例
# nova flavor-show c6.large.2 | grep -i numa
| extra_specs | {"hw:cpu_policy": "dedicated", "NIC:multiqueue": "2", "hw:mem_page_size": "1GB", "hw:numa_nodes": "1", "NIC:PPS": "bbbbbbbb-bbbb-bbbb-bbbb-000000000040", "NIC:QoS": "aaaaaaaa-aaaa-aaaa-aaaa-000000004096", "hw": "compute-optimized"} |
# nova flavor-key c6.large.2 set hw:numa_nodes=2
#根据计算节点node数进行设置
# nova flavor-show c6.large.2 | grep -i numa
| extra_specs | {"hw:cpu_policy": "dedicated", "NIC:multiqueue": "2", "hw:mem_page_size": "1GB", "hw:numa_nodes": "2", "NIC:PPS": "bbbbbbbb-bbbb-bbbb-bbbb-000000000040", "NIC:QoS": "aaaaaaaa-aaaa-aaaa-aaaa-000000004096", "hw": "compute-optimized"} |
ixgbe网卡驱动 Ⅳ----收发包流程详解
https://blog.csdn.net/wangquan1992/article/details/117450280
Linux网络编程6——poll和epoll
https://blog.csdn.net/HuanBianCheng27/article/details/126166492
689

被折叠的 条评论
为什么被折叠?



