linux内存映射(Memorymapping)

标签:
itkernellinux杂谈 |
Memory Management in Linux
Address Types
Linux
是个虚拟内存操作系统,用户进程看到的内存没有直接对应硬件使用的物理地址,虚拟地址产生了一个转换层,这导致了一些很好的特性,使用虚拟地址,系统上的程序可以分配远多于实际物理内存的内存空间。
Linux的地址类型包括:
mmap Overview
在Linux内核,映射内核地址空间到用户地址空间是可能的,这消除了复制用户空间信息到到内核空间的开销,反之也一样,这是通过设备驱动和用户空间设备接口(
/dev
)来实现。这个功能可以用来在设备驱动struct
file_operations结构中
实现mmap()操作,以及在用户空间的系统调用的
mmap()实现。
虚拟内存的基本管理单元是页(page),通常大小是4k,但在某些平台可以达到64k,在任何时候处理虚拟内存时,要处理2类地址:虚拟地址和物理地址,所有的CPU访问
(including
from kernel
space)使用虚拟地址,会在页表的帮助下被MMu转换为物理地址,内存的物理页面由页帧号标识
(PFN),
PFN很容易由物理地址除以页面大小来得到
(or by shifting the physical address with PAGE_SHIFT bits to the
right).
基于效率的原因,虚拟地址空间被分为用户空间和内核空间,同样的理由,内核空间包含内存映射区域,称作lowmem,这是一个连续的映射的物理内存,从最低的物理地址
(usually
0)开始,lowmem被映射的虚拟地址由
PAGE_OFFSET定义。
在32位系统中,不是所有的物理内存都能被映射到
lowmem,因为在内核空间有个独立的区域,称为
highmem,这可以用来任意地映射物理内存。kmalloc()分配的内存驻留在
lowmem,它是持续的内存区域,vmalloc()分配的内存是不来连续的,并且不驻留在
lowmem,
(it has a dedicated zone in highmem).Structures used for memory mapping
这里将讨论Linux内核内存管理子系统相关的基本数据结构,包括,
struct
page
, struct
vm_area_struct
, struct
mm_struct
.struct page
struct
page用于嵌入有关系统中所有物理页面的信息,在系统中对所有的物理页面内核有一个
struct
page结构。和这个结构相关的函数包括:
===========================
virt_to_page()
returns the page associated with a virtual addresspfn_to_page()
returns the page associated with a page frame numberpage_to_pfn()
return the page frame number associated with astruct page
page_address()
returns the virtual address of astruc page
; this functions can be called only for pages from lowmemkmap()
creates a mapping in kernel for an arbitrary physical page (can be from highmem) and returns a virtual address that can be used to directly reference the page
==========================
struct
vm_area_struct
struct
vm_area_struct保持一片连续内存区域的信息,进程的内存区域可以通过检查在
procfs的进程的maps属性来查看:
====================
root@qemux86:~# cat /proc/1/maps
#address
perms offset
device
inode
pathname
08048000-08050000 r-xp 00000000 fe:00
761
/sbin/init.sysvinit
08050000-08051000 r--p 00007000 fe:00
761
/sbin/init.sysvinit
08051000-08052000 rw-p 00008000 fe:00
761
/sbin/init.sysvinit
092e1000-09302000 rw-p 00000000 00:00
0
[heap]
4480c000-4482e000 r-xp 00000000 fe:00
576
/lib/ld-2.25.so
4482e000-4482f000 r--p 00021000 fe:00
576
/lib/ld-2.25.so
4482f000-44830000 rw-p 00022000 fe:00
576
/lib/ld-2.25.so
44832000-449a9000 r-xp 00000000 fe:00
581
/lib/libc-2.25.so
449a9000-449ab000 r--p 00176000 fe:00
581
/lib/libc-2.25.so
449ab000-449ac000 rw-p 00178000 fe:00
581
/lib/libc-2.25.so
449ac000-449af000 rw-p 00000000 00:00 0
b7761000-b7763000 rw-p 00000000 00:00 0
b7763000-b7766000 r--p 00000000 00:00
0
[vvar]
b7766000-b7767000 r-xp 00000000 00:00
0
[vdso]
bfa15000-bfa36000 rw-p 00000000 00:00
0
[stack]
=====================
内存区域由
start address, stop
address, length, permissions确定。
struct
vm_area_struct在每一次
从用户空间的mmap()调用时创建,支持
mmap()操作的驱动必须完成和初始化
struct
vm_area_struct结构,此结构最终要的域包括:
===================
vm_start
,vm_end
- 内存区域的起始和终止地址(these fields also appear in/proc//maps
);vm_file
- the pointer to the associated file structure (if any);vm_pgoff
- the offset of the area within the file;vm_flags
- a set of flags;vm_ops
- a set of working functions for this areavm_next
,vm_prev
- the areas of the same process are chained by a list structure
=======================
struct mm_struct
struct
mm_struct结构包含了一个进程的所有内存区域,
struct
task_struct结构的mm域是指向当前进程的
struct
mm_struct结构的指针。
Device driver memory mapping
内存映射是unix系统最令人感兴趣的特色之一,从设备驱动的哦观点看,内存映射工具允许对用户空间设备的直接内存访问。
为了分配
mmap()
操作给设备驱动,设备驱动的struct
file_operations结构的
mmap
域必须实现,在这种情况下,用户空间进程可以使用与设备关联的文件描述符上的
mmap这个系统调用。
mmap()系统调用带有下列参数:
=================
void *mmap(caddr_t addr, size_t len, int prot, int flags, int fd, off_t offset);
=================
为了在用户空间和设备间映射内存,用户进程必须打开设备并以该文件描述符为参数发起
mmap()系统调用:
===================================
int (*mmap)(struct file *filp, struct vm_area_struct *vma);
=========================
filp是指向在用户空间打开的设备的
struct
file的指针,
vma参数是内存必须被设备映射的内存的虚拟地址空间。设备必须分配内存(using
kmalloc()
,
vmalloc()
,
alloc_pages()
)并映射它到由vma参数指示的用户地址空间,在函数remap_pfn_range()的帮助下。
remap_pfn_range()函数将映射一段连续的物理地址空间到
vm_area_struct结构表述的虚拟地址空间。
===================
int remap_pfn_range (structure vm_area_struct *vma, unsigned long addr, unsigned long pfn, unsigned long size, pgprot_t prot);
==========================
remap_pfn_range()的相关参数解析:
------------------------------
- vma - the virtual memory space in which mapping is made;
- addr - the virtual address space from where remapping begins; page tables for the virtual address space between addr and addr + size will be formed as needed
- pfn the page frame number to which the virtual address should be mapped
- size - the size (in bytes) of the memory to be mapped
- prot - protection flags for this mapping
-----------------------------
下面的例子使用该函数映射连续内存地址为页帧号pfn
(memory that was previously
allocated)的物理地址到
vma->vm_start虚拟地址。=========================
struct vm_area_struct *vma;
unsigned long len = vma->vm_end - vma->vm_start;
int ret ;
ret = remap_pfn_range(vma, vma->vm_start, pfn, len, vma->vm_page_prot);
if (ret < 0) {
pr_err("could not map the address area\n");
return
-EIO;
}
unsigned long len = vma->vm_end - vma->vm_start;
int ret ;
ret = remap_pfn_range(vma, vma->vm_start, pfn, len, vma->vm_page_prot);
if (ret < 0) {
}
===========================
为了获得物理内存的页面帧号,必须考虑内存你是如何分配的,对每一个
kmalloc()
,
vmalloc()
,
alloc_pages()
,
必须考虑不同的方法,对kmalloc(),可以考虑如下
--------------------------------
static char *kmalloc_area;
unsigned long pfn = virt_to_phys((void
*)kmalloc_area)>>PAGE_SHIFT;
--------------------------------
对
vmalloc()
=====================
static char *vmalloc_area;
unsigned long pfn =
vmalloc_to_pfn(vmalloc_area);
====================
alloc_pages()
:=====================
struct page *page;
unsigned long pfn = page_to_pfn(page);
unsigned long pfn = page_to_pfn(page);
====================
因为页面被映射到用户空间,可能被交换到缓冲区,为了避免此现象,必须对此物理页面设置PG_reserved位,调用
函数
SetPageReserved()设置,调用函数
ClearPageReserved()清除此位。
=====================
void alloc_mmap_pages(int npages)
{
int i;
char *mem =
kmalloc(PAGE_SIZE * npages);
if
(!mem)
return mem;
for(i = 0; i
< npages * PAGE_SIZE; i += PAGE_SIZE) {
SetPageReserved(virt_to_page(((unsigned long)mem) + i));
return
mem;
}
void free_mmap_pages(void *mem, int npages)
{
int i;
for(i = 0; i
< npages * PAGE_SIZE; i += PAGE_SIZE) {
ClearPageReserved(virt_to_page(((unsigned long)mem) + i));
kfree(mem);
}
========================
前一篇:龙芯1b工具链解压问题
后一篇:LINUXsmp多处理器系统