Memory Mapping and DMA [LDD3 15]

Memory Management in LinuxAddress TypesLinux kernel中有很多的地址类型,比如最简单的就是虚拟地址和物理地址。在用户程序中看到的地址都是虚拟地址,和硬件使用的物理地址不同。kernel中的地址类型如下:User virtual addressesuser space programmer看到的地址就是用户态虚拟地址,可能是32bit,...
摘要由CSDN通过智能技术生成

Table of Contents

15.1. Memory Management in Linux

15.1.1. Address Types

15.1.2. Physical Addresses and Pages

15.1.3. High and Low Memory

15.1.4. The Memory Map and Struct Page

15.1.5. Page Tables

15.1.6. Virtual Memory Areas

15.1.6.1 The vm_area_struct structure

15.1.7. The Process Memory Map

15.2. The mmap Device Operation

15.2.1. Using remap_pfn_range

15.2.2. A Simple Implementation

15.2.3. Adding VMA Operations

15.2.4. Mapping Memory with nopage

15.2.5. Remapping Specific I/O Regions

15.2.6. Remapping RAM

15.2.6.1 Remapping RAM with the nopage method

15.2.7. Remapping Kernel Virtual Addresses

15.3. Performing Direct I/O

15.3.1. Asynchronous I/O

15.4. Direct Memory Access

15.4.1. Overview of a DMA Data Transfer

15.4.2. Allocating the DMA Buffer

15.4.2.1 Do-it-yourself allocation

15.4.3. Bus Addresses

15.4.4. The Generic DMA Layer

15.4.4.1 Dealing with difficult hardware

15.4.4.2 DMA mappings

15.4.4.3 Setting up coherent DMA mappings

15.4.4.4 DMA pools

15.4.4.5 Setting up streaming DMA mappings

15.4.4.6 Single-page streaming mappings

15.4.4.7 Scatter/gather mappings

15.4.4.8 PCI double-address cycle mappings

15.4.4.9 A simple PCI DMA example

15.4.5. DMA for ISA Devices

15.4.5.1 Registering DMA usage

15.4.5.2 Talking to the DMA controller


本章的内容分为三节:

1, mmap的实现,也就是把device memory map到user process里,这样可以提高performance。

2, kernel driver如何访问user space的page。

3, DMA,也就是device直接访问system memory。

15.1. Memory Management in Linux


15.1.1. Address Types

Linux kernel中有很多的地址类型,主要分为两类:虚拟地址,物理地址。在用户程序中看到的地址都是虚拟地址,和硬件使用的物理地址不同,虚拟地址并不直接对应物理地址,需要中转,采用虚拟地址的机制,可以让程序使用比物理地址多得多的内存。

kernel中的地址类型其实还需要细分,虚拟地址包括几种类型如下:

User virtual addresses

user space programmer看到的地址就是用户态虚拟地址,一般是32bit或者64bit,取决于当前的硬件架构,每个进程都有自己的虚拟地址空间。

Physical addresses

在处理器和系统内存之间,使用的就是物理地址。物理地址也分为32bit或者64bit,即便32bit系统,在某些条件下也能使用很大的物理内存。

Bus addresses

在外设总线和物理内存之间,使用的就是bus address。通常和CPU使用的物理地址相同,但是如果有IOMMU,就不一样了。IOMMU会把物理地址做一个map,拿到的地址就是bus address,device可以通过这个bus address做DMA。

Kernel logical addresses

这个就是kernel自己的normal地址空间,一般会把物理内存map成normal地址空间,并且可以当作物理地址空间来使用,某些架构上,kernel的逻辑地址和物理地址差一个offset,逻辑地址通常使用硬件相关的native pointer,指针能访问多少memory,逻辑地址空间就能支持多少memory,通常这个pointer是一个unsigned long或者void *类型,因此如果在32bit系统上,就可能访问不了大的内存地址。kmalloc返回的就是逻辑地址。

Kernel virtual addresses

kernel虚拟地址和逻辑地址有些类似,都是最终map到物理地址,区别在于虚拟地址对应的物理地址可能不是连续的,不是一一映射。逻辑地址是虚拟地址的子集,即所有的逻辑地址都是虚拟地址,但不是所有的虚拟地址都是逻辑地址。例如,vmalloc和kmap返回的就是虚拟地址,它对应的物理内存可能不是连续的,kmalloc返回的地址就是逻辑地址,它分出来的page都是连续的。虚拟地址通常使用指针变量来存储。

如果你有一个逻辑地址,可以通过__pa()获取到它对应的物理地址;物理地址也可以通过__va()获取它的逻辑地址,但只限于low memory page,不能用于high-memory page。

在kernel的函数中,不同的接口可能需要不同的地址类型,这个需要自己特别注意。

15.1.2. Physical Addresses and Pages

kernel中的物理内存,都是按照page来管理,每一个page都是PAGE_SIZE这么大,PAGE_SIZE具体的值取决与硬件架构,一般是4096 byte。无论虚拟地址(逻辑地址?)还是物理地址,地址的组成都分成了两个部分:PFN(page frame number)和offset,假设先的PAGE_SIZE是4096byte,那么低12bit就是作为page中的offset,如果把这低12bit向右移出去,得到的值就是PFN。获取pfn的操作在kernel中很常见,具体移出去多少bit,取决于PAGE_SHIFT这个值。

15.1.3. High and Low Memory

kernel的虚拟地址和逻辑地址的区别在大容量物理内存的32位机器上笔记明显,理论上讲,32位机器上可以寻址4G的地址空间,但是因为kernel设置虚拟地址的方式,实际值比4G要小。

按照默认设置,32位的机器上,4G的地址空间划分为user space和kernel space,user space占用了3G的虚拟地址空间,kernel space占用1G的虚拟地址空间。在kernel 1G的地址空间里,除了kernel的code、data等占用的空间之外,能够map出来使用的地址空间不到1G。kernel里,如果没有对应的虚拟地址,kernel是无法访问这段memory的,因此kernel实际能够访问的内存实际上是1G减掉kernel code等自己占用的部分,也就是不到1G。如果是大容量的内存,就会导致很多的物理地址不能map到kernel的地址空间,从而不能使用。后来CPU中添加了feature,添加了内存扩展特性,从而使得CPU可以访问到超过4G的内存。但是kernel中的逻辑地址仍然有这个限制,只能map优先的物理内存,超过这部分的memory称为high memory,而kernel map过能直接访问的memory就是low memory,也就是在kernel中直接就存在逻辑地址。这里对high、low memory又做了定义:

Low memroy

kernel中可以使用的物理内存,这部分内存在kernel中有逻辑地址相对应,被称为low memory。

High memory

这部分内存在kernel中不存在直接能够访问的逻辑地址,因为地址范围超过了kernel的虚拟地址空间反问,被称为high memory。

kernel中low memory和hight memory的分界线在1G memory以下的某个位置。

15.1.4. The Memory Map and Struct Page

因为历史原因,kernel一直使用逻辑地址来访问物理内存中的page,因此对于hight memory,这种访问就有问题了,因为high memory在kernel中没有能直接访问的逻辑地址。因此kernel中对page的访问越来越多的使用struct page这个结构体,这个结构体中的成员有:

atomic_t count;

page的reference counter,如果变成了0,就被放入free page list。

void *virtual; 

如果这个page被map过,记录的就是map后得到的虚拟地址,否则就是NULL。low memory通常都是被map过的,high memory通常没有被map。这个成员在有些架构上没有,因为他们有更好的计算虚拟地址的方法。

unsigned long flags;

用于描述page的属性和状态,比如PG_locked,说明page在memory中已经被lock;PG_reserved,说明page已经被reserve,kernel的内存管理不应该再touch这个page。

kernel中使用struct page的数组来管理物理内存,有些系统上只有一个数组mem_map,有些架构的系统上,比如NUMA,因为有大量不连续的物理内存,那就可能需要多个page 数组来管理这些内存。幸运的是,driver只需要使用struct page即可,不需要关心这个page从哪里来的。kernel提供了一些函数,可以方便的根据page获取virtual address:

#include <linux/mm.h>
#include <linux/highmem.h>
#include <asm/kmap_types.h>

//根据逻辑地址获取对应的page结构体,如果kaddr是从vmalloc或者high memory过来的,这个不能用。
struct page *virt_to_page(void *kaddr);
//根据pfn,获取它对应的page 结构体。
struct page *pfn_to_page(int pfn);
//返回这个page对应的kernel虚拟地址。如果是high memory,需要事先map过才行。一般不用,而是用kmap。
void *page_address(struct page *page);

#include <linux/highmem.h>
//kmap返回这个page对应的虚拟地址,如果是low memory,直接就是逻辑地址
//如果是high memory并且没有map过,kernel就会给它在专用的space里做一次map,然后返回虚拟地址。
//kmap对同一个page有reference counter,所以要和kunmap配对调用。
void *kmap(struct page *page);
void kunmap(struct page *page);

//是kmap的高性能版本,有些架构上会reserver一些专用的slots(PTE),给atomic用。
//参数type用来表明需要哪个slot,driver能够使用的slot一般是KM_USER0和KM_USR1(如果是在user space的系统调用),以及KM_IRQ0和KM_IRQ1(interrupt handler)。
//调用atomic的driver code必须是atomic的,不能sleep。
void *kmap_atomic(struct page *page, enum km_type type);
void kunmap_atomic(void *addr, enum km_type type);

15.1.5. Page Tables

kernel中既然使用了虚拟地址,必然存在某种机制,可以通过虚拟地址得到物理地址,这个机制就是page table,page table也许是多级数据结构来实现,并且包含一些对应的flag。device driver的很多操作可能都会涉及page table,但是kernel已经做了封装,driver不需要和page table直接打交道,这里也不再赘述。

15.1.6. Virtual Memory Areas

virtual memory area (VMA)是kernel中的结构体,用于区分和管理进程地址空间中的不同区域。一个VMA中代表了一类有相同访问权限或者底层对应了同一个object的连续虚拟地址空间,大概类似于segment的概念。一个进程地址空间中通常包含以下几个部分:

1, 存储程序代码的区域。

2, 存放数据的区域,通常有多个,比如已经初始化的变量数据,还有未初始化的数据变量,以及程序的stack等。

3, 活动中的memory mapping的一个区域。

通过/proc/<pid/maps>可以看到这个进程虚拟地址空间的状态,读取maps,打印出来的东西有几个部分:

start-end perm offset major:minor inode image

看个例子:

# 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

除了image,其他每一列在kernel的struct vm_area_struct中都有对应的成员变量(除了image name):

start
end

这个memory area的start和end虚拟地址。

perm

这个memory area对应的读写或者执行权限,表示进程针对虚拟地址对应的page所能做的操作。最后的字符要么是p,表示private,要么是s,表示shared。

offset

表示当前的VMA,在这个被map的file中的起始offset,offset为0意味着VMA的start对应了file的start。

major
minor

代表了device的major/minor number,这个device就是使用了这个被map的file的device。

inode

被map的文件对应的inode。

image

被map的文件的文件名,通常是可执行文件。

15.1.6.1 The vm_area_struct structure

当用户态进程调用了mmap去map device memory的时候,kernel就会给它创建一个新的vm_area_struct结构体。底层的device driver需要实现mmap,mmap的功能其实就是帮助kernel初始化这个VMA。

下面我们就看一下vm_area_struct这个结构体里的成员变量,其中有些可能会被device driver用到。要注意的是vm_area_struct里有一些成员变量kernel用来存储VMA的list或者树形结构,因此这个结构体不能在device driver中创建,而是由kernel自己创建。其中,比较重要的member有:

unsigned long vm_start;
unsigned long vm_end;

这个VMA的start和end,也就是/proc/pid/maps里看到的start和end。

struct file *vm_file;

如果area有关联的 file,指向它。

unsigned long vm_pgoff;

被map的file的offset,按照page来算的,不是byte。当file后者device memory被map的时候,这个就是第一个被map的page的位置。

unsigned long vm_flags;

用来描述这个vma的flags。最重要的两个flag是VM_IO和VM_RESERVED。VM_IO表示这个vma是用来做I/O的memroy map,在做core dump的时候会跳过这个vma。VM_RESERVED告诉kernel不要把vma swap出去,在大部分device的map中都会设置这个flag。

struct vm_operations_struct *vm_ops;

kernel用来操作这个vma一系列函数。说明vma在kernel中也是类似于struct file的object(对应一系列的callback,类似于面向对象)。

void *vm_private_data;

driver用来存储自己的私有数据。

下面是vm_operations_struct中的callback:

void (*open)(struct vm_area_struct *vma);

当这个vma有新的reference时(比如fork),kernel会调用实现这个VMA的subsystem的open callback,用来对vma做一些初始化。如果这个vma第一次create是通过mmap产生的,open就不会被调用,而是去调用driver实现的mmap。

void (*close)(struct vm_area_struct *vma);

当vma被destroy的时候,kernel会调用vma的close callback。要注意的是,vma本身没有记reference count,process只会调用一次open和close。

struct page *(*nopage)(struct vm_area_struct *vma, unsigned long address, int *type);

当进程试图访问一个在有效的vma里面,但是当前不在memory里的page时,kernel会调用nopage这个callback。如果这个page是被swap到了别的存储设备,nopage会把这个page再swap进来,并返回struct page的指针。如果nopage callback是NULL,kernel会分配一个空的page。

int (*populate)(struct vm_area_struct *vm, unsigned long address, unsigned long len, pgprot_t prot, unsigned long pgoff, int nonblock);

当user 访问memory,kernel可以提前发生fault,driver一般不实现。(没太理解&#

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Mapping and charting solutions是一种可以用来制作地图和图表的软件工具。它可以帮助用户将自己的数据制作成各种类型的图表或地图,以便更好地理解和分析数据。 该软件可以用于各种领域,包括业务分析、市场研究、科学研究等。用户可以通过设置不同的参数来定义图表或地图的样式和格式,比如颜色、字体等。 此外,mapping and charting solutions还提供了数据可视化的功能,可以将数据以直观、易懂的形式展现出来,帮助用户更好地理解数据关系。通过这种方式,用户可以更快、更准确地分析数据,发现可能存在的问题,并采取相应的措施。 总之,mapping and charting solutions是一种非常有用和有效的数据可视化工具。它为用户提供了一种快速、直观的展现数据的方式,使得数据分析变得更加轻松和高效。 ### 回答2: 如果您需要下载mapping和charting solutions,您可以从以下几个途径获取: 1. 在官方网站上下载:许多mapping and charting solutions的开发商会在其官方网站上提供下载链接。您可以通过谷歌或其他搜索引擎搜索特定的mapping and charting solutions,然后找到它们的官方网站并在该网站上找到下载选项。 2. 在应用商店下载:一些mapping and charting solutions也会有相应的应用程序,您可以在各大应用商店(如苹果的App Store、安卓的Google Play商店)中搜索并下载这些应用。 3. 在开源社区下载:许多mapping and charting solutions是以开源的形式发布的,您可以在GitHub等开源社区找到并下载这些解决方案。在这些平台上,您可以通过搜索相关的关键字来找到开源的mapping and charting solutions,然后按照指示下载并使用它们。 无论您选择哪种方式下载mapping and charting solutions,建议您在下载和使用之前先了解其功能、使用规则和操作步骤,以便更好地利用这些解决方案来进行地图制作和图表绘制。个别mapping and charting solutions可能需要购买授权或注册才能完全使用,所以请在下载之前了解相关许可证的要求。 ### 回答3: mapping and charting solutions可以被理解为地图制作和绘图解决方案。这样的解决方案通常提供了各种工具和技术,用于创建、编辑和呈现地图和图表。它们能够将地理数据可视化,从而帮助用户更好地理解和分析地理信息。 对于专业测绘人员、地理信息系统(GIS)专家、地理学家和城市规划师来说,mapping and charting solutions是非常重要的工具。它们使他们能够创建高质量的地图,并进行准确的测量和绘制。 mapping and charting solutions通常提供的功能包括: 1. 数据收集和整理:通过收集和整理地理数据,用户可以构建具有准确性和可靠性的地图和图表。工具可以通过不同的方式收集数据,如GPS测量、遥感技术、航空摄影等。 2. 数据编辑和渲染:这些解决方案提供了一系列编辑工具,用于编辑地理数据,包括添加和删除地点、绘制和修改线条、多边形和面。用户还可以选择适当的符号和颜色来呈现地理元素。 3. 数据分析和可视化:mapping and charting solutions可以帮助用户分析地理数据,并将其呈现为可视化的图表。这些图表可以包括统计数据、热力图、线条图等。 4. 地图输出和共享:用户可以将地图和图表输出为不同的格式,如印刷版地图、电子文档或在线地图。这样,用户可以与其他人共享地理信息,并在不同的设备上查看和导航。 总而言之,mapping and charting solutions是为了帮助用户创建、编辑和呈现地图和图表,并提供数据收集、编辑、分析和共享功能的专业工具。这些解决方案在测绘、GIS和地理学等领域被广泛使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值