Linux下高级内存操作技巧:ioremap详解 (linux高端内存 ioremap)

在Linux操作系统中,内存映射技术是非常重要的一种内存操作方法。因为它能够将物理内存映射到虚拟地址空间中,从而方便对其进行读写操作。而其中一个重要的内存映射函数是ioremap()。本文将对Linux下的ioremap函数进行详细讲解和操作实例。

一、ioremap函数介绍

ioremap()函数是Linux内核中用来将物理地址映射到虚拟地址空间的函数。在物理内存空间没有映射到当前进程的虚拟地址空间时,可以通过ioremap来完成映射。

ioremap函数的原型定义如下:

“`

void *ioremap(resource_size_t phys_addr, unsigned long size);

“`

其中,phys_addr为物理地址,size为映射的大小。该函数返回值为映射到的虚拟地址。

需要注意的是,ioremap()函数只能用于IO设备的地址映射,不能用于普通内存的地址映射。因为普通内存的地址映射应该通过内核的虚拟内存管理来实现,而不是通过ioremap()函数。

二、ioremap操作实例

下面通过一个实例来说明Linux下如何使用ioremap函数实现对物理内存的操作。

假设当前有一个系统板卡,其有一个物理地址区域为0x20230000~0x20230000。现在需要在Linux内核中将该区域映射到虚拟地址空间中,以便对其进行读写操作。

代码实现如下:

“`c

#define MEM_ADDR 0x20230000

#define MEM_SIZE 0x100000

void __iomem *g_mem_base;

static int __init test_init(void)

{

g_mem_base = ioremap(MEM_ADDR, MEM_SIZE);

if (!g_mem_base) {

printk(KERN_ERR “ioremap fled\n”);

return -ENOMEM;

}

printk(KERN_INFO “ioremap succeeded\n”);

//对物理内存进行读写操作

void *mem = (void __force *)g_mem_base;

unsigned int value = ioread32(mem + 0x1c);

iowrite32(value + 1, mem + 0x1c);

return 0;

}

static void __exit test_exit(void)

{

iounmap(g_mem_base);

printk(KERN_INFO “iounmap succeeded\n”);

}

module_init(test_init);

module_exit(test_exit);

MODULE_LICENSE(“GPL”);

“`

其中,使用__iomem修饰符告诉编译器,g_mem_base指针指向的是物理内存,防止编译器进行一些优化。

在module_init()函数中,使用ioremap函数实现对指定地址区域的内存映射。并在其中对物理内存进行了一次读写操作,即读取指定内存地址的值,并将该值加1写回原位。

在module_exit()函数中,使用iounmap()函数释放通过ioremap()映射的虚拟地址空间。

三、使用ioremap函数需要注意的事项

1. ioremap()函数映射的内存空间属于内存映射区域,需要对其进行页对齐。

2. ioreadXX()和iowriteXX()函数是用于读写物理地址空间的函数,其中的XX表示读写数据的长度,如ioread32和iowrite32表示读写32位数据。需要注意的是,访问这些地址区域的方式需要是原子的。

3. 避免在进入中断上下文时使用ioremap()函数,可以使用ioremap_wc()函数进行操作。

四、

通过本文,我们详细介绍了Linux下高级内存操作技巧中重要的ioremap函数。并通过实例来演示了如何使用该函数实现对物理内存的操作。需要注意的是,ioremap()函数只能用于IO设备的地址映射,并且需要保证访问该地址的操作是原子的。在实际使用过程中需要注意相关的事项。

相关问题拓展阅读:

驱动中操作物理绝对地址为什么要先ioremap

void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags) 入口: phys_addr:要映射的起始的IO地址; size:要映射的空间的大小; flags:要映射的IO空间的和权限有关的标志; 功能: 将一个IO地址空间映射到内核的虚拟地址空间上去,便于访问; 实现:对要映射的IO地址空间进行判断,低PCI/ISA地址不需要重新映射,也不允许用户将IO地址空间映射到正在使用的RAM中,最后申请一 个 vm_area_struct结构,调用remap_area_pages填写页表,若填写过程不成功则释放申请的vm_area_struct空 间; 意义: 比如isa设备和pci设备,或者是fb,硬件的跳线或者是物理连接方式决定了硬件上的内存影射到的cpu物理地址。 在内核访问这些地址必须分配给这段内存以虚拟地址,这正是__ioremap的意义所在 ,需要注意的是,物理内存已经”存在”了,无需alloc page给这段地址了. 文件中的注释也是比较详尽的,并且只 暴露了__ioremap,iounmap两个函数供其他模 块调用,函数remap_area_pte,remap_area_pmd,remap_area_pages只为__ioremap所用. -------- 为了使软件访问I/O内存,必须为设备分配虚拟地址.这就是ioremap的工作.这个函数专门用来为I/O内存区域分配虚拟地址(空间).对于直接映射的I/O地址ioremap不做任何事情(uClinux中是这么实现的??) 有了ioremap(和iounmap),设备就可以访问任高搜何I/O内存空间,不论它是否直接映射到虚拟地址空间.但是戚让历,这些地址永远不能直接使用(指物理地址),而要用readb这种函数. 根据计算机平台和所使用总线的不同,I/O 内存可能是,也可能不是通过页表访问的,通过页表访问的是统一编址(PowerPC),否则是独立编址(Intel)。如果访问是经由页表进行的,内核必须首先安排物理地址使其对设备驱动 程序可见(这通常意味着在进行任何 I/O 之前必须先调用 ioremap)。如果访问无需页表,那么 I/O 内存区域就很象 I/O 端口,可以使 用适当形式的函数读写它们。 不管访问 I/O 内存时是否需要调用 ioremap,都不鼓励直接使用指向 I/O 内存的指针。尽管(在“I/O 端口和 I/O 内存” 介绍过)I/O 内存在硬件一级是象普通 RAM 一样寻址的,但在“I/O 寄存器和常规内存”中描述过的那些需要额外小心的情况中已经建议不要使用普 通指针。相反,使用“包装的”函数访问 I/O 内存,一方面在所有平台上都是安全的,另一方面,在可以直接对指针指向的内存区域执行操作的时候,该函数 是经过优化的 ------- 自己原以为当给显卡上的存储空间分配了总线地址A以后,它所对应的虚拟空间就随之确定了.也就是A+3G.可是事实上,在ioremap.c文件里面的实现并不是这样的.所用的函数是 __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags)实现的时候是为从phys_addr开始的size大小的物理地址分配一块虚拟地址.注意这里是分配,而不是指定.我所认为的分配应该是指定即根据phys_addr得到其所对应的滑含虚拟地址是phys_addr+3G. 本人认为一合理的解释是这样的:系统虚拟空间中映射的非IO卡上的地址空间满足3G差关系,而IO卡上的 存储空间就不满足了.

如何在linux内核中保留一块内存不被系统分配

要想在内核中保留一个块内存,不被系统分配,而是只由自己使用,可以使用以下函数

reserve_bootmem(addr,size,flag)

这个函数的三个参数,addr是你要保基族吵留的地址,size就是要保留的大小,flag是标志位,要来确定是否检查你要保搏侍留的空间是否已经被保留了,它可以设置为两个值,BOOTMEM_DEFAULT和BOOTMEM_EXCLUSIVE,前者就是总是会返回0(也就是保留成功),而后者则会检查你所要保留的空间是否已经被保留了,若已经被保留了,则它返回失败(负数)

那么这个函数一般放在哪里使用过了,我们建议放到内核init/main.c中,在页表的建立之前进行保留,比如我们下面的代码,放在main中的start_kernel函数中,我们的内核是2.6.32

alinkage void __init start_kernel(void)

{

……

……

trap_init();

if(reserve_bootmem(0x,0x100000,BOOTMEM_EXCLUSIVE) != 0){

printk(“reserve fail—-\n”);

}

printk(“we reserve boot meme of 0xfor gps—–\n”);

mm_init();

……

……

}

这样就保留了一个起始地址穗渗为0x,大小为1M的空间

在开机保留内存的方式一共有三种方法:

1. reserve_bootmem(addr,size,flag)

这种方法,我做实验一直没有成功…

如果有成功的,希望大神留言,求指导!

2. alloc_bootmem(size)

其实还是比较简单的,在int/main.c中:

//设置全局变量

char* reserve_memory;

EXPORT_SYMBOL_GPL(reserve_memory);

void __init start_kernel()

{

trap_init();

//在此添加滚裂代码,在zone保留40M的内存

reserve_memory=alloc_bootmem(0x);

mem_init();

}

但是在内核中得到reserve_memory之后,如何映射到内核空间中使用,没有理出头绪…

网上搜到有人说,

有了allco_bootmem返回的地址,

在戚清内核态,找到大仔闭要使用这段内存的task_struct;

进而强行建立页表;

如果有大神理解这句话,或者更好的映射方法,请留言,谢谢!

3. 开机时设置Linux 内核参数

mem=180M /* 开机时只给系统180M的内存空间,其余都保留 */

这种方法最为简单,但是设置和使用的时候,需要和硬件内存物理大小相适应。

在内核驱动中,可以以ioremap()的方式映射到内核空间中,或者再mmap()到用户空间中读写。

Linux里面什么线性内存?

Linux内存线性地址空间格局解析

实用平台:i386

Linux内存线性地址空间大小为4GB,分为2个局部:用户空间局部(等闲是3G)和内核空间局部(等闲是1G)。在此我们重要关怀内核地址空间局部。

内核穿越内核页大局目录来管教所有的物理内存,由于线形地址前3G空间为用户利用,内核页大局目录前768项(刚好3G)除0、1两项外全副为0,后256项(1G)用来管教所有的物理内存。内核页大局目录在编译时静态地定义为swapper_pg_dir数组,该数组从物理内存地址0x101000处开始储藏。

由图可见,内核线形地址空间局部从PAGE_OFFSET(等闲定义为3G)开始,为了将内核装入内存,从PAGE_OFFSET开始8M线形地址用来照射内核所在的物理内存地址;接下来是mem_map数组,mem_map的起始线形地址与系统构造相干,例如对于UMA构造,由于从PAGE_SIZE开始16M线形地址空间对应的16M物理地址空间是DMA区,mem_map数组等闲开始于PAGE_SIZE+16M的线形地址;从PAGE_SIZE开始到VMALLOC_START

VMALLOC_OFFSET的线形地址空间直接照射到物理内存空间(一一对应影射,物理地址=线形地址-PAGE_OFFSET),这段区域的大小和机器切实具有的物理内存大小有关,这儿VMALLOC_OFFSET在x86上为8M,重要用来遏止越界讹谬;在内存比拟小的系统上,余下的线形地址空间(还要再扣除空白区即VMALLOC_OFFSET)被vmalloc()函数用来把不继续的物理地址空间照射到继续的线形地址空间上,在内存比拟大的系统上祥链,vmalloc()利用从VMALLOC_START到VMALLOC_END(也即PKMAP_BASE扣除2页的空白页大小PAGE_SIZE)的线形地址空间,此刻余下的线形地址空间(还要再扣除2页的空白区让雀即VMALLOC_OFFSET)又能够分成2局部:之一局部从PKMAP_BASE到FIXADDR_START用来由kmap()函坦宴早数照射高端内存;第二局部,从FIXADDR_START到FIXADDR_TOP,这是一个安宁大小的线形地址空间,(引用:Fixed

virtual addresses are needed for subsystems that need to know the

virtual address at compile time such as the

APIC),在x86系统构造上,FIXADDR_TOP被静态定义为0xFFFFE000,此刻这个安宁大小空间告终于全副线形地址空间最后4K前面,该安宁大小空间大小是在编译时计算出来并存储在__FIXADDR_SIZE变量中。真空断路器o:p>

正是由于vmalloc()利用区、kmap()利用区及安宁大小区的存在才使ZONE_NORMAL区大小受到局限,由于内核在运行时必需这些函数,因而在线形地址空间中起码要VMALLOC_RESERVE大小的空间。VMALLOC_RESERVE的大小与系统构造相干,在x86上,VMALLOC_RESERVE定义为128M,这即便为什么我们看到ZONE_NORMAL大小等闲是16M到896M的起因。

linux高端内存 ioremap的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于linux高端内存 ioremap,Linux下高级内存操作技巧:ioremap详解,驱动中操作物理绝对地址为什么要先ioremap,如何在linux内核中保留一块内存不被系统分配,Linux里面什么线性内存?的信息别忘了在本站进行查找喔。


数据运维技术 » Linux下高级内存操作技巧:ioremap详解 (linux高端内存 ioremap)