美文网首页
Linux内核 内存映射 & DMA

Linux内核 内存映射 & DMA

作者: 偷油考拉 | 来源:发表于2021-08-21 22:40 被阅读0次

Linux Device Drivers, Third Edition [LWN.net]
第十五章 Memory Mapping and DMA

Address Types

Linux是一个虚拟内存系统,用户程序与硬件使用的物理地址不直接对应。虚拟内存引入了一层间接寻址,从而支持很多高级特性。通过虚拟内存,系统上可以给运行程序分配大于实际物理内存的内存空间;甚至,单个进程也可以拥有比物理内存大的虚拟地址地址空间。虚拟内存允许程序使用进程地址空间做一些高级操作,比如将程序内存映射到设备内存。

Linux系统有好几种类型的地址,每个地址都有自己的语义。但是,有时候内核不知道哪种情况下使用哪种类型的地址,因此程序员必须小心。

下面是Linux中使用的地址类型列表,以及于物理内存的关系。

  • User virtual addresses 用户虚拟地址
    用户空间程序看到的常规地址(regular addresses)。用户地址为32位或64位长度,具体取决于底层硬件架构,每个进程都有自己的虚拟地址空间。

  • Physical addresses 物理地址
    处理器和系统内存交互使用的地址。物理的地址是32位或64位的数量;某些情况下,32位系统也可以使用更大的物理地址。

  • Bus addresses 总线地址
    总线和内存交互使用的地址。通常,与处理器使用的物理地址相同,但也不一定。某些架构可以提供I/O内存管理单元(IOMMU),在总线和主存之间重新映射地址。IOMMU可以通过多种方式使得更简单,但必须在设置DMA时对IOMMU进行编程。总线地址,高度依赖架构。

  • Kernel logical addresses 内核逻辑地址
    这些组成了内核地址空间的normal部分。这些地址映射主内存的一部分(也许是全部),通常被视为物理地址。在大多数架构上,逻辑地址和它们关联的物理地址仅差一个固定的偏移量。逻辑地址使用硬件的原生指针大小,因此在32位系统上可能无法给所有的物理内存映射地址。逻辑地址通常存储在unsigned longvoid*类型的变量中。kmalloc返回的内存有一个内核逻辑地址。

  • Kernel virtual addresses 内核虚拟地址
    内核虚拟地址类似与逻辑地址,都是从内核空间地址到物理地址的映射。内核虚拟地址
    不一定是线性的、一对一的映射,这些是逻辑地址的特征。逻辑地址一定是内核地址虚拟地址,但内核虚拟地址不一定是逻辑地址。
    例如,vmalloc分配的内存有一个虚拟地址(不是直接到物理内存的映射)。kmap也返回虚拟地址。虚拟地址通常存储在指针变量中。

图片.png

Physical Addresses and Pages

物理内存被分成很多离散单元,称为页(page)。系统对内存的大部分内部处理基于页。页大小因架构而异,大多是4096-byte。常量PAGE_SIZE(定义在<asm/PAGE.h>)可以在任何架构上定义页大小。

如果查看地址(虚拟or物理),它被划分为一个页码(page number)和一个偏移量(offset)。如果使用4k页面,则低12位为偏移量,其余高位表示页码。

如果放弃该偏移并将其余向右移动,结果称为PFN(page frame number)。在PFN和地址之间转换是常见操作;宏PAGE_SHIFT告诉此转换须偏移多少位。

物理页通过Page Frame Number (PFN)识别。PFN可以从物理地址轻易计算出来,除以page size 或 去掉PAGE_SHIFT bits。

PFN计算:
PFN = (physical address / page frame size) 取整。余数是page frame offset。

通常,page frames大小等于virtual pages。

High and Low Memory

在32位系统上配备很多内存,逻辑地址和内核虚拟地址之间就差异明显了。使用32位,只可以寻址4 GB内存。
32位Linux系统因为其虚拟地址空间的设置方式,直到现在还被限制在比这少的内存上。

x86架构默认配置下,内核划分4GB虚拟地址空间给用户空间和内核空间;它们的contexts中使用相同的映射集。一个典型的划分方式:3 GB用于用户空间,1 GB用于内核空间。

内核的代码和data structures必须加载到该空间,但是使用内核地址空间最多的是到物理内存的虚拟映射。内核不能直接操作未映射到内核地址空间的内存。换句话说,内核需要它自己的虚拟地址来存放它必须直接接触的内存。
因此,多年来,内核可以处理的最大物理内存量是:可以映射到内核虚拟地址空间部分的内存量,减去内核代码本身所需的空间。所i有,x86 Linux 最多只可以使用略低于1GB的物理内存。

不破坏32位程序和系统兼容性,还能支持更多内存呢,就成了商业压力。为此,处理器制造商在产品中提供了地址扩展(address extension)功能。这样,32位处理器也可以寻址超过4GB的物理内存。

然而,可以被直接映射到逻辑地址的物理内存限制仍然存在。只有内存的最低位部分(最多1或2 GB,取决于硬件和内核配置)具有逻辑地址;其余的(高位内存)没有。在访问特定的高位内存页之前,内核必须设置显式虚拟映射,以使该页在内核地址空间中可用。

因此,许多内核data structures必须放在低位内存中;高位内存倾向于为用户空间进程页保留。

high memory一词可能会让一些人感到困惑,特别是因为它在PC世界中有其他含义。因此,我们在这里作术语定义:

  • Low memory
    在内核空间中存在逻辑地址的内存。几乎每个系统上,所有内存都是low memory。

  • High memory
    不存在逻辑地址的内存,因为它超出了为内核虚拟地址预留的地址范围。

在i386系统上,低位内存和高位内存之间的边界通常设置为略低于1GB,但是可以在内核配置更改。此边界仅于内核设置有关,与硬件无关。

Page Tables

现代系统,处理器都必须具备相应的机制将虚拟地址转换为物理地址制。该机制称为页表 (page table);本质上,它是一个多级树状数组,包含虚拟-物理的映射和一些相关的标志(flags)。Linux内核甚至在不直接使用页表的架构上,也维护着一组页表。

通常,设备驱动程序执行的许多操作都可能涉及到操作页表。幸运的是,2.6内核已经不再直接使用页表。
推荐阅读《Understanding The Linux Kernel》(O’Reilly),作者 Daniel P. Bovet and Marco Cesati 。

Virtual Memory Areas

VMA - 虚拟内存区域,是用于管理进程地址空间的不同区域的内核数据结构(kernel data structure)

VMA维护着进程虚拟内存中的一个由相同类型组成的区域:具有相同权限标志的,后端是同一对象(例如文件或交换空间)的,连续虚拟地址范围。

它大致上对应于segment(段)的概念,不过最好将其描述为“具有自身属性的内存对象”。进程的内存映射由(至少)以下区域组成:

  • 程序可执行代码的区域(通常称为文本 - text)

  • 多个数据区域,包括初始化数据(在执行开始时具有显式赋值的数据)、未初始化数据(BSS),和程序堆栈

  • 每个活动的内存映射一个区域

通过查看/proc/<pid/maps>可以看到进程的内存区域。/proc/self/proc/pid的特例,它对应当前进程。范例:

# cat /proc/1/maps   
               look at init
08048000-0804e000 r-xp 00000000 03:01 64652      /sbin/init  #text
0804e000-0804f000 rw-p 00006000 03:01 64652      /sbin/init  #data
0804f000-08053000 rwxp 00000000 00:00 0          #zero-mapped BSS
40000000-40015000 r-xp 00000000 03:01 96278      /lib/ld-2.3.2.so  #text
40015000-40016000 rw-p 00014000 03:01 96278      /lib/ld-2.3.2.so  #data
40016000-40017000 rw-p 00000000 00:00 0          #BSS for ld.so
42000000-4212e000 r-xp 00000000 03:01 80290      /lib/tls/libc-2.3.2.so  #text
4212e000-42131000 rw-p 0012e000 03:01 80290      /lib/tls/libc-2.3.2.so  #data
42131000-42133000 rw-p 00000000 00:00 0          #BSS for libc
bffff000-c0000000 rwxp 00000000 00:00 0          #Stack segment
ffffe000-fffff000 ---p 00000000 00:00 0          #vsyscall page

# rsh wolf cat /proc/self/maps  #### x86-64 (trimmed)
00400000-00405000 r-xp 00000000 03:01 1596291     /bin/cat    #text
00504000-00505000 rw-p 00004000 03:01 1596291     /bin/cat    #data
00505000-00526000 rwxp 00505000 00:00 0                       #bss
3252200000-3252214000 r-xp 00000000 03:01 1237890 /lib64/ld-2.3.3.so
3252300000-3252301000 r--p 00100000 03:01 1237890 /lib64/ld-2.3.3.so
3252301000-3252302000 rw-p 00101000 03:01 1237890 /lib64/ld-2.3.3.so
7fbfffe000-7fc0000000 rw-p 7fbfffe000 00:00 0                 #stack
ffffffffff600000-ffffffffffe00000 ---p 00000000 00:00 0       #vsyscall

相关文章

  • Linux内核 内存映射 & DMA

    Linux Device Drivers, Third Edition [LWN.net][https://lwn...

  • Linux内核如何管理少于1GB的物理内存

    我们在学习Linux内存管理时了解到: Linux内核将 0MB ~ 896MB 物理内存,映射为 0xC0000...

  • Linux内核内存管理专题训练营

    【Linux内核内存管理专题训练营】 最新Linux内核技术详解 独家Linux内核内存管理干货分享 两天持续技术...

  • Linux - 用户态内存映射 和 内核态内存映射

    操作系统的内存管理,主要分为三个方面。第一,物理内存的管理,相当于会议室管理员管理会议室。第二,虚拟地址的管理,也...

  • Nio中文件映射和零拷贝

    NIO中文件映射和零拷贝 Zero-Copy DMA从拷贝至内核缓冲区 cpu将数据从内核缓冲区拷贝至内核空间(s...

  • Linux内核与发行版

    Linux内核 Linux内核是什么 Linux内核是Linux操作系统的核心, 负责硬件管理, 比如管理内存、管...

  • 网络收发包流程

    一 收包流程 内核分配一个主内存地址段(DMA缓冲区 RingBuffer),网卡设备可以在DMA缓冲区中读写...

  • Java NIO

    基本概念 缓冲区的操作用户空间缓冲区内核空间缓冲区DMA用户空间相对于内核空间的限制 -发散汇聚 虚拟内存物理内存...

  • NIO

    传统IO拷贝 4次 切换 3次DMA直接内存拷贝(不使用CPU)mmap优化 内存映射 4次切换 3次拷贝send...

  • mmap原理

    即memory map(内存映射)。内存映射减少了文件从内核空间copy到用户空间,但是增加了文件映射到用户空间的...

网友评论

      本文标题:Linux内核 内存映射 & DMA

      本文链接:https://www.haomeiwen.com/subject/uwgfiltx.html