物流配送管理制度软件哪个比较好?大家推荐一下

& 求教ioremap和phys_to_virt有什么区别和关系?
声明: 本页内容为的内容镜像,文章的版权以及其他所有的相关权利属于和相应文章的作者,如果转载,请注明文章来源及相关版权信息。
(finished)
(finished)
(finished)
求教ioremap和phys_to_virt有什么区别和关系?
[ | 78 byte(s)]
[ | 566 byte(s)]
[ | 267 byte(s)]
[ | 171 byte(s)]
我是新手,不明白ioremap和phys_to_virt这两个函数有什么区别,分别可以用在什么场合?
ioremap是用来为IO内存建立映射的, 它为IO内存分配了虚拟地址,这样驱动程序才可以访问这块内存。
phys_to_virt, 只是计算出某个已知物理地址所对应的虚拟地址。
天吖度绱耍疫M杯中物!
主要还是跟体系结构关系密切,有些I/O是内存映射的体系结构,比如MIPS,ioremap也只是通过加一个偏移,简单的将物理地址映射成虚拟地址,这样就跟phys_to_virt没太大区别了。但是在类似于x86这样的体系结构上,I/O地址是端口号形式的,就不能简单的通过加偏移来映射I/O地址到虚拟地址了。
我的blog:/juventus
刚好那天查资料的时候看了一下ioremap,今天又顺便又看了一下,大体简述一下
我2.6的只有电子版,看的是只有2.4是纸钞版的老皇历了
phys_to_virt()实际上是就是__va(),将物理地址转化成虚拟地址,简单的加上个偏移量3G,所以这个东东只能
用在内核里面.见/include/asm-i386/io.h
ioremap()是将外设卡上的内存映射到内存上,实现虚拟空间的管理.原来的名字是vremap().
基本的意思是将指定的物理地址映射到虚拟地址上去.这个虚拟地址由内核来管理,同样从内核中分配页面,init_mm来管理页目录,页表项等,如同用户进程一样.最后返回虚拟地址,见/usrc/arch/i386/mm/ioremap.c
诗圣曰:语不惊人死不休我亦曰:技不动人死不休
输入您的搜索字词
提交搜索表单
unixresources.net
Copyright &
UNIX Resources Network, All Rights Reserved.
About URN | Privacy & Legal | Help | Contact us
webmaster:
This page created on
16:26:51, cost 0.7 ms.了解linux内存映射的大虾!展示你能力的机会来了,请听一道100分的问题!!!
来源:csdn
【嘿嘿,其实是小弟在在开发过程中遇到关于内存映射的问题,不知道怎么解决。还请大神帮忙!!
arm9 + linux 环境,板子RAM大小为256M, 目前有个需求是将内存从后面去掉2M,让系统跑在254M内存上,最后的2M由一个内核ko模块去操作,存放一些打印LOG,设备重启后log不丢失(设备异常重启,RAM不掉电)。
目前已经成功预留出了2M内存,这2M空间不受内核管理,即系统以为RAM大小只有254M, 但是要在一个自己写的内核ko模块中去操作这2M内存。小弟试过直接由物理地址转换虚拟地址(函数phys_to_virt)然后去访问,但是一访问就会产生缺页异常。
听别人说是因为 这2M内存没有被内核映射,故不能正常访问。那么就要我去手动进行映射。但是具体该怎么去映射呢?
还请大侠指点一二,小弟将感激不尽!!!】
IOMAP文件中加这几句
这是个例子,具体地址,自己改吧。
#define _BASE
IOMEM(0xFC900000)
#define _PHYS
#define _SIZE
banks0913:
意思是说将这2M RAM 当做IO内存,然后让MMU去映射,是吧?
edwardlulinux:
这预留的2M大小也是要映射的。关键是你前面的254M的空间是怎么去映射的。然后又是如何处理预留的2M的空间。
任何通过cpu去操作的地址都必须过mmu这关(除非是nommu)。如果你上电初始化map的过程没有处理想要预留的2M空间的话,p2v和v2p函数是不能操作的,应为page table没有这段空间的映射关系。mmu查表fail 引起异常。然后你把异常的地址看看通过简单减去偏移量的关系看看是不是你想要的物理内存的位置。
参考的函数是iotable init这个函数。
banks0913:
谢谢您耐心的回答!
1.那么您的意思是让我按照内核映射前面254M内存的方法去映射这2M,是吗?
2.再问一句,如果我把这2M当做设备寄存器一样,用ioremap的方式去映射成IO内存去访问,是否合理?
yanghao23:
保留内存在kernel传递ATAG参数时可以少传入这部分内存,这部分内存还不是被保存了,只是没用告诉kernel这部分内存,kernel中需要使用时ioremap是一种方法(比较简单),但是更好的方式是ion,
banks0913:
ion?愿闻其详。小弟没听说过,也没搜到什么相关信息,还请大侠给解释一下
edwardlulinux:
其实网上有篇文章写的是静态映射。具体的网页地址我忘记了。但是确实有这么一片文章,描写的内容和我们现在讨论的问题相关。
静态映射 iotable init
动态映射 ioremap
其实这个量个函数调用的方法不一样,但是目的都是一样的。就是建立以个虚拟到phy的对应关系。
2.再问一句,如果我把这2M当做设备寄存器一样,用ioremap的方式去映射成IO内存去访问,是否合理?
这个可以的。只是:1 调用到ioremap的这个函数你可能自己要写意个module。2 调用的参数和使用的方法有区别,我熟悉所以推荐了静态映射的方法,这个方法当时用过,用在VIDEO的还是什么的一个buffer缓存上。
其实kernel中还有的板子的bsp中有很多类似的用法,学习和借鉴一下别人的用法对自己很有帮助。说实话,我也是经常忘记自己做过什么。
yanghao23:
ion在最新android kernel中已经大范围使用了,我不确定kernel主分支是否已加入该特性,
ION是下一代内存管理,Ion支持连续和非连续的分配,它支持的内存分配有:CARVOUT(PMEM style), physically contiguous, virtually contiguous but physically discontiguous, IOMMU, security-sensitive memory.
ION提供了API给kernel和用户空间程序,用户空间的程序通过设备节点/dev/ion来创建一个client
@ION_HEAP_TYPE_CARVEOUT:
memory allocated from a prereserved
carveout heap, allocations are physically
contiguous
自己学习下吧,
在kernel 的board-XXX.c文件中MACHINE_START 的.reserve函数中会去设定那些内存作为保留内存,还可以
调用 memblock_reserve()
保留内存,
楼主2M内存怎么预留的,通过引导参数吗?
免责声明:本站部分内容、图片、文字、视频等来自于互联网,仅供大家学习与交流。相关内容如涉嫌侵犯您的知识产权或其他合法权益,请向本站发送有效通知,我们会及时处理。反馈邮箱&&&&。
学生服务号
在线咨询,奖学金返现,名师点评,等你来互动内核源代码研究区
ioremap内存映射 始终不能访问内存 求解
ioremap内存映射,修改了数次,还是不能正常访问寄存器,基本问题是可以加载模块,但是只要lsmod就会出现段错误
unable&to&handle&kernel&paging&request&at&virtual&address&7fXXXXXX
望高手指教!!!
#include&&asm/io.h&
#include&&linux/module.h&
#include&&linux/kernel.h&
#include&&linux/fs.h&
#include&&linux/init.h&
#include&&linux/delay.h&
#include&&asm/irq.h&
#include&&mach/regs-gpio.h&
#include&&mach/hardware.h&
#include&&linux/device.h&
#include&&linux/gpio.h&
static&int&led_
static&void&*
#define&LED_CONTROLLER_BASE&0x
#define&led_all_off 0
#define&led_all_on 1
#define&DEVICE_NAME "zx_led"
#define&LED_CON&(*(volatile&u32*)(LED_CONTROLLER_BASE))
#define&LED_DAT&(*(volatile&u32*)(LED_CONTROLLER_BASE&+&4))
#define&LED_UP (*(volatile&u32*)(LED_CONTROLLER_BASE&+&8))
#define&LED_MEM_LEN 0x0c
static&int&led_open(struct&inode&*inode,struct&file&*file)
printk("the&led&driver&is&open\n");
static&int&led_close(struct&inode&*inode,struct&file&*file)
printk("the&led&driver&is&close\n");
static&int&led_ioctl(struct&inode&*inode,struct&file&*file,unsigned&int&cmd,unsigned&long&arg)
switch(cmd)
case&led_all_off:&
//iowrite16(0x00,base&+&0x04);
iowrite16((ioread16(base&+&0x04)&0xff)|0x0,base&+&0x04);
case&led_all_on:
iowrite16(0x0f00,base&+&0x04);
iowrite16((ioread16(base&+&0x04)&0xff)|0xff00,base&+&0x04);
return&-EINVAL;
static&struct&file_operations&led_fops&=
.owner =& THIS_MODULE,
.open = led_open,
.ioctl = led_ioctl,
.release= led_close,
static&struct&class&*led_
static&int&led_init(void)
printk("led&initialize\n");
led_major&=&register_chrdev(0,&DEVICE_NAME,&&led_fops);
if(led_major&&&0)
printk("can't&creat&led_major\n");
return&led_
printk("register&zhengxu_led&Driver&OK!&Major&=&%d\n",&led_major);
led_class&=&class_create(THIS_MODULE,DEVICE_NAME);
if(IS_ERR(led_class))
printk("led_class&create&is&failed\n");
return&-1;
printk("class_create&is&ok\n");
device_create(led_class,&NULL,&MKDEV(led_major,&0),&NULL,&DEVICE_NAME);
printk("initialize&is&sucessful\n");
request_mem_region(LED_CONTROLLER_BASE,LED_MEM_LEN,"zx_led");
base=ioremap_nocache(LED_CONTROLLER_BASE,LED_MEM_LEN);
printk("%lx\n",base);
printk("ioremap&is&sucessful\n");
if(base&&&0)
printk("ioremap&is&failed\n");
// iowrite16(0xff00,base);
iowrite16((ioread16(base)&0xff00)|0x00ff,base);
// iowrite16(0xff00,base&+&0x08);
iowrite16((ioread16(base&+&0x08)&0xff)|0xff00,base&+&0x08);
void&led_exit(void)
unregister_chrdev(led_major,DEVICE_NAME);
device_destroy(led_class,MKDEV(led_major,0));
class_destroy(led_class);
iounmap(base);
release_mem_region(LED_CONTROLLER_BASE,LED_MEM_LEN);
module_init(led_init);
module_exit(led_exit);
MODULE_AUTHOR("zhengxu");
MODULE_LICENSE("GPL");
引用楼主&isolated_bb&的回复:unable&to&handle&kernel&paging&request&at&virtual&address&7fXXXXXX
另[&code=C/C++&]代码是复制到这的[&/code&]
unable&to&handle&kernel&paging&request&at&virtual&address&9f3008f7
pgd&=&c18b0000
[9f3008f7]&*pgd&=&
INternal&error&:&oops&5&[#1]
unable&to&handle&kernel&paging&request&at&virtual&address&9f3008f7
pgd&=&c18b0000
[9f3008f7]&*pgd&=&
INternal&error&:&oops&5&[#1]
以上就是错误信息
使用gdb定位下,看看是哪句话出了问题。
把错误多贴一点,感觉不是ioremap内存不能访问,&ioremap用来映射IO的.
是可以用ioremap去映射物理内存的,不过base的类型好像是&void&__iomem&*啊!还有啊,最好多用printk打印程序执行到哪儿出问题了,我一般直接用ioremap。
可以这样定义后就可以直接读值和写值,就把它当做一个变量就可以了。不过你也可以直接搞成结构体类型也成。
#define&ADCCON
(*(volatile&unsigned&long&*)(base_addr&+&0x00))
首先&感谢大家回复!!&&(arm(2440)+linux)
我所粘贴的信息是我串口打印出来的全部的错误信息了,我也不知道具体的错误在哪里,但是只要注释掉ioremap就没有任何问题了(当然内存也无法映射了),因为编译的时候没有什么错误信息提示,加载内核模块的时候(insmod)也没有,只是查询模块挂载(lsmod)或者卸载模块的时候(rmmod)就会出现以上的错误信息
对于6楼的话我不是很理解,直接定义访问?那么访问的是I/O端口还是I/O内存?我认为你访问的是端口,I/O内存不是必须经过映射转化为虚拟地址才能访问么?ARM体系中不存在I/O端口吧?所以只要是寄存器的访问通通都要先映射?后访问虚拟内存空间??我程序的意思也是这样,先将寄存器的物理地址映射到虚拟内存空间,然后对虚拟内存空间进行访问,从而达到访问寄存器的目的!!!并且ioremap的返回地址也是对的(C4848050)是在3G以上的内核空间!!可是错误信息中提示"无法处理此时的请求页面调度的内核虚拟地址9f3008f7,这个是一个用户空间的地址",我是真不知道是怎么个情况了.....以上是个人理解&不知道对不对&还望请教&继续讨论
该回复于 10:52:41被版主删除
6楼说的是不是这个意思&直接对这个端口变量进行赋值从而达到对寄存器的访问???
我随便写了个例子
(*(volatile&unsigned&long&*)S3C2410_GPBDAT)&=&0xXX
这里的S3C2410_GPBDAT并不是你端口的实际地址吧(物理地址)??它需要有BSP(板级支持包)的支持,然后映射到虚拟地址区域的一个虚拟地址&所以它本质上应该还是个虚拟地址也就是0xc0000000以上的地址&虽然内核中没有对它进行ioremap的映射,是因为ARM体系结构是统一编制的??寄存器与内核地址空间本身就存在着固定地址的偏移??所以这样的操作也是对虚拟地址操作从而控制寄存器的状态!!
而我的目的是通过ioremap函数来映射到内核地址空间,然后修改虚拟地址空间达到控制寄存器的状态的目的,如果用BSP支持的话,我是可以正常访问的,但是如果没有BSP支持呢?我就是想实验下ioremap函数,不知确出现这么多的问题......
以上只属个人意见,不对之处,欢迎指出,本人虚心接受
你没有检测request_mem_region这函数返回值,它可能不能申请到那块内存空间,这样当然会失败
我在/proc/iomem下可以看到申请的端口&并且我也添加了打印信息&所以应该不是这个问题&但还是谢谢你的回答
if(!request_mem_region(LED_CONTROLLER_BASE,LED_MEM_LEN,"zx_led"))
&&&&&&&&printk("request_mem_region&failed,pls&check&LED_CONTROLLER_BASE.\n");
&&&&&&&&return&-1;
引用&9&楼&isolated_bb&的回复:6楼说的是不是这个意思&直接对这个端口变量进行赋值从而达到对寄存器的访问???
我随便写了个例子
(*(volatile&unsigned&long&*)S3C2410_GPBDAT)&=&0xXX
这里的S3C2410_GPBDAT并不是你端口的实际地址吧(物理地址)??它需要有BSP(板级支持包)的支持,然后映射到虚拟地址区域的一个虚拟地址&所以它本质上应该还是个虚拟地址也就是0xc0……
S3C2410_GPBDAT就是ioremap返回的虚拟地址。
你说的意思我明白,我没有试过采用ioreab16这个来读取寄存器值,而是将ioremap后的指针直接使用的。如
base_addr=&ioremap(xx,xx);
#define&xx&&(*(volatile&unsigned&long&*)(base_addr&+&0x00))这样来用的。
然后赋值采用xx&=&1;和temp=xx来实现寄存器的读写。不过应该还可以使用
struct&regs
&&&&&unsigned&long&reg1;
&&&&&unsigned&long&reg2;
&&&&&&&&....
struct&regs&*
register=(struct&regs&*)base_
不过这个我没有试过,理论上是可以的,因为base_addr是内核虚拟地址。
呃....我明白你的意思了&但是直接对指针赋值的话&我看了很多参考文献&都说这样会很不好...一定要用标准的IO函数...并且应该不是&访问IO的问题&我再好好检查一下我的指针吧&也许不是ioremap的问题
是不是你的ioremap的第二个参数不太对?
请问楼主解决了问题了没,&如果解决了,&希望能共享一下答案。&拜托了
回复
即使是一小步也想与你分享linux内存映射相关知识点
我的图书馆
linux内存映射相关知识点
说明:此文档综合了网上很多文章,并结合自己的分析,综合《情景分析》。里面的代码是网上的,尚未亲自验证,有时间了好好搞搞,若用版权问题,请及时通知,务必删除,谢谢。
1.外设内存资源
通常,在系统运行时,外设的I/O内存资源的物理地址是已知的,由硬件的设计决定。
2.CPU对外设内存资源的访问
&&&&&& 对外部设备的访问有两种不同的形式:
?&&&&&&&&I/O映射方式(I/O-mapped)
典型地,如X86处理器为外设专门实现了一个单独的地址空间,称为"I/O地址空间"或者"I/O端口空间",这个存储空间与内存分属两个不同的体系,CPU无法通过访问内存的指令而只能通过专门的I/O指令(如X86的IN和OUT指令)来访问这一空间中的地址单元;
?&&&&&&&&内存映射方式(Memory-mapped)
RISC指令系统的CPU(如ARM、PowerPC等)通常只实现一个物理地址空间,外设I/O端口成为内存的一部分。此时,CPU可以象访问一个内存单元那样访问外设I/O端口,而不需要设立专门的外设I/O指令。
3.Linux下对外设内存资源的操作
需要注意:
?&&&&&&&&CPU并没有为这些已知的外设I/O内存资源的物理地址预定义虚拟地址范围;
?&&&&&&&&Linux下,驱动程序并不能直接通过物理地址访问I/O内存资源,而必须将它们映射到核心虚地址空间内(通过页表),然后才能根据映射所得到的核心虚地址范围,通过访问内存指令访问这些I/O内存资源;
&&&&&& Linux在io.h头文件中声明了函数ioremap(),用来将I/O内存资源的物理地址映射到核心虚地址空间(3GB-4GB)中,原型如下:
[cpp] static&inline&void&__iomem&*&ioremap&(unsigned&longoffset,&unsigned&long&size)&&{&&&&&return__ioremap(offset,&size,&0);&&}&&
&&&&&& 在将I/O内存资源的物理地址映射成虚地址后,理论上讲我们就可以象读写RAM那样直接读写I/O内存资源了。但为了保证驱动程序的跨平台的可移植性,我们应该使用Linux中特定的函数来访问I/O内存资源,而不应该通过指向核心虚地址的指针来访问。如在x86平台上,读写I/O的函数如下所示:
[cpp] #define&readb(addr)&(*(volatile&unsigned&char&*)__io_virt(addr))&&#define&readw(addr)&(*(volatile&unsigned&short&*)__io_virt(addr))&&#define&readl(addr)&(*(volatile&unsigned&int&*)__io_virt(addr))&&&&&#define&writeb(b,addr)&(*(volatile&unsigned&char&*)__io_virt(addr)&=&(b))&&#define&writew(b,addr)&(*(volatile&unsigned&short*)&__io_virt(addr)&=&(b))&&#define&writel(b,addr)&(*(volatile&unsigned&int&*)__io_virt(addr)&=&(b))&&&&&#define&memset_io(a,b,c)memset(__io_virt(a),(b),(c))&&#define&memcpy_fromio(a,b,c)memcpy((a),__io_virt(b),(c))&&#define&memcpy_toio(a,b,c)memcpy(__io_virt(a),(b),(c))&&
需要注意:
&&&&&& 驱动中可能并没有ioremap函数,但是一定存在外设物理地址到内核虚拟地址间的转化过程,达到的效果是一样的。
4.利用mmap()操作设备内存
&&&&&& 用mmap映射一个设备,意味着用户空间的一段地址关联到设备内存上,使得用户程序在分配的地址范围内进行读取或者写入,实际上就是对设备的访问。
&&&&&& 参考第7节:样例程序。
5.利用/dev/mem操作设备内存
/dev/mem相当于整个系统的内存(包括系统内存和设备内存和MMIO)的一个映射文件。通过/dev/mem设备文件和mmap系统调用,可以将线性地址描述的物理内存映射到进程&&的地址空间,然后就可以直接访问这段内存了。用法一般就是open,然后mmap,接着可以使用map之后的地址来访问物理内存。可作为实现用户空间驱动的一种方法。
两个例子:
1)操作PCI设备
通过/proc/bus/pci获得相应的PCI设备的配置寄存器,再获得相应的物理地址,然后通过调用/dev/mem的mmap方法就可以了。
2)操作显存
比如,标准VGA&16色模式的实模式地址是A000:0000,而线性地址则是A0000。设定显&&存大小为0x10000,则可以如下操作&&
[cpp] mem_fd&&=&open(&"/dev/mem",&O_RDWR&);&&&&&&vga_mem&=&mmap(&0,&0x10000,&PROT_READ&|&PROT_WRITE,&MAP_SHARED,&&&&&&&&&&&&&&&&&&&&&&&&mem_fd,&0xA0000&);&&&&&&close(&mem_fd&);&&&&
然后直接对vga_mem进行访问就可以了。当然,如果是操作VGA显卡,还要获得I/O&&端口的访问权限,以便进行直接的I/O操作,用来设置模式/调色板/选择位面等等&在
3)系统保留内存
这种方法可用来在内核和应用程序之间高效传递数据:&&
?&&&&&&&&假定系统有64M物理内存,则可以通过uboot通知内核只使用63M,而保留1M物理内存作为数据交换使用(使用&mem=63M&标记);&
?&&&&&&&&然后打开/dev/mem设备,并将63M开始的1M地址空间映射到进程的地址空间;
样例程序:
[cpp] #include&&stdio.h&&&#include&&sys/types.h&&&#include&&sys/stat.h&&&#include&&fcntl.h&&&#include&&stdlib.h&&&#include&&unistd.h&&&#include&&sys/mman.h&&&&&&int&p_addr,&v_&&volatile&unsigned&char&*paddr,&*&&&&&int&main(int&argc,&char&*argv[])&&{&&&&&int&&&&&&int&i,&j,va,&&&&&&int&bw,&&&&&&charlcmdbuf[128];&&&&&char*cmdbuf,&&&&&&p_addr&=0x4c000000;&&&&&if(argc&1){&&&&&&&&&p_addr&=&strtoul(argv[1],&NULL,&16);&&&&&}&&&&&&&&&fd&=open("/dev/mem",&O_RDWR|O_SYNC);&&&&&if(fd&0){&&&&&&&&&printf("Error&open&/dev/mem!");&&&&&&&&&return&-1;&&&&&&}&&&&&&v_addr&=(int)mmap(NULL,&4096,&PROT_READ|PROT_WRITE,&MAP_SHARED,&fd,&&p_addr&~0x00000fff);&&&&&if(v_addr&0){&&&&&&&&&printf("Unableto&mmap&%08x!",&p_addr);&&&&&&&&&return-1;&&&&&}else{&&&&&&&&&printf("Mappaddr&%08x&to&%08x!",&p_addr,&v_addr);&&&&&};&&&&&&&&&paddr&=(char*)p_&&&&&&vaddr&=(char*)v_&&&&&&bw&=&1;&&&&&&cp&=&0;&&&&&while(1){&&&&&&&&&if(cp==0){&&&&&&&&&&&&&printf("-");&&&&&&&&&&&&&fgets((char*)lcmdbuf,&128,&stdin);&&&&&&&&&}&&&&&&&&&cmdbuf=&lcmdbuf+&&&&&&&&&while(*cmdbuf==''){&&&&&&&&&&&&&cmdbuf++;&&&&&&&&&}&&&&&&&&&&&&do{&&&&&&&&&&&&&ch=&lcmdbuf[cp];&&&&&&&&&&&&&cp++;&&&&&&&&&}while(ch!=0&&&ch!=''&&&&ch!=''&&&&ch!=';');&&&&&&&&&lcmdbuf[cp-1]=&0;&&&&&&&&&if(ch!=';'){&&&&&&&&&&&&&cp=&0;&&&&&&&&&}&&&&&&&&&&&&if(cmdbuf[0]=='q')&&&&&&&&&&&&&&&&&&&&&&if(cmdbuf[1]=='b'){&&&&&&&&&&&&&bw=&1;&&&&&&&&&}elseif(cmdbuf[1]=='w'){&&&&&&&&&&&&&bw=&2;&&&&&&&&&}elseif(cmdbuf[1]=='d'){&&&&&&&&&&&&&bw=&4;&&&&&&&&&}&&&&&&&&&switch(cmdbuf[0]){&&&&&&&&&case'd':&&&&&&&&&&&&&sscanf((char*)cmdbuf+2,"%x",&&va);&&&&&&&&&&&&&for(j=0;j&256;&j+=16){&&&&&&&&&&&&&&&&&printf("%08x:",&(int)paddr+va+j);&&&&&&&&&&&&&&&&&for(i=0;i&16;&i+=bw){&&&&&&&&&&&&&&&&&&&&&if(bw==1){&&&&&&&&&&&&&&&&&&&&&&&&&printf("%02x",&vaddr[va+i+j]);&&&&&&&&&&&&&&&&&&&&&}elseif(bw==2){&&&&&&&&&&&&&&&&&&&&&&&&&printf("%04x",&*(unsigned&short*)&vaddr[va+i+j]);&&&&&&&&&&&&&&&&&&&&&}elseif(bw==4){&&&&&&&&&&&&&&&&&&&&&&&&&printf("%08x",&*(unsigned&int*)&vaddr[va+i+j]);&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&printf("");&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&&&&&&case'r':&&&&&&&&&&&&&sscanf((char*)cmdbuf+2,"%x",&&va);&&&&&&&&&&&&&if(bw==1){&&&&&&&&&&&&&&&&&printf("%08x=&%02x",&(int)paddr+va,&*(vaddr+va));&&&&&&&&&&&&&}elseif(bw==2){&&&&&&&&&&&&&&&&&printf("%08x=&%04x",&(int)paddr+va,&*(unsigned&short*)(vaddr+va));&&&&&&&&&&&&&}elseif(bw==4){&&&&&&&&&&&&&&&&&printf("%08x=&%08x",&(int)paddr+va,&*(unsigned&int*)(vaddr+va));&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&&&&&&case'w':&&&&&&&&&&&&&sscanf((char*)cmdbuf+2,"%x&%x",&&va,&&vd);&&&&&&&&&&&&&if(bw==1){&&&&&&&&&&&&&&&&&*(vaddr+va)=&(unsigned&char)&&&&&&&&&&&&&}elseif(bw==2){&&&&&&&&&&&&&&&&&*(unsignedshort*)(vaddr+va)&=&(unsigned&short)&&&&&&&&&&&&&}elseif(bw==4){&&&&&&&&&&&&&&&&&*(unsignedint*)(vaddr+va)&=&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&&&&&&&default:&&&&&&&&&&&&&&&&&&&&&&}&&&&&}&&&&&return&0;&&}&&
6. kmalloc()和vmalloc() 函数
kmalloc和vmalloc分配内存最大的不同在于,kmalloc能分配到物理上连续的页,所以kmalloc得到的地址也称为“逻辑地址”(因为是连续的页,所以访问物理内存只需要一个偏移量计算即可,速度快)。系统运行久了以后,连续的地址当然变少,如果在这个时候,分配大片内存,kmalloc得不到满足,而可能需要内核进行移动页面等操作,无益于系统内存的利用和管理。vmalloc分配内存时,不考虑物理内存中是否连续,而使用一个表来转换虚拟地址与物理地址的关系。在分配大内存的时候,vmalloc成功率高,也很好地利用了内存空间。
总之:kmalloc分配到连续的物理内存页,而vmalloc则不连续
7. remap_page_range和remap_vmalloc_range函数
[cpp] int&remap_pfn_range(struct&vm_area_struct&*vma,unsigned&long&addr,&unsigned&long&pfn,&unsigned&long&size,&pgprot_t&prot);&&
[cpp] &span&style="font-family:&Arial,&Verdana,&sans-&white-space:&&background-color:&rgb(255,&255,&255);&"&这个函数就完成“将内核空间的地址与页的对应关系,转化为用户空间中的对应关系”。pfn是Page&Frame&Number的缩写,即表示一个页的编号。从函数名称便可以看出,它”remap”一个”range”的”pfn”,就是重新映射一个范围的页表。也就是只能映射连续的页。因此这个函数只适用于连续的物理内存页(即kmalloc或者__get_free_pages获得的)&/span&&&
如果不连续的页怎么办?(vmalloc分配的空间)
这种情况可以使用内核提供的vm_operations_struct结构。其结构如下 :
[cpp] struct&vm_operations_struct&{&&&&&&&&&&void(*open)(struct&vm_area_struct&*&area);&&&&&&&&&&void(*close)(struct&vm_area_struct&*&area);&&&&&&&&&&int&(*fault)(struct&vm_area_struct&*vma,struct&vm_fault&*vmf);&&&&/*&.....*/&&}&&
其中的fault原型,指出了内核在找不到某个地址对应的页时,调用的函数。由于页不连续,不能使用remap_pfn_range,即没有建立地址和页的对应关系,所以在MMAP后,用户访问该范围的某地址时,肯定会发生缺页异常,即找不到页!这时会调用fault函数,由驱动来负责寻找这页!怎么找呢?首先,我们可以计算一下,用户试图访问的这个地址,离映射起始地址的偏移 offset;然后,通过这个偏移 offset,我们可以得到内核空间中的地址(通过与vmalloc得出的地址相加即可);最后,通过vmalloc_to_page函数,得到我们找到的内核虚拟地址对应的页表。这就是这个用户地址所对应的页表。
示例代码:
[cpp] void&*&kernel_space_&/*&将来在某地分配&*/&&unsigned&long&kernel_space_&/*&指定分配空间的大小&*/&&&&&static&int&vma_fault(struct&vm_area_struct&*vma,struct&vm_fault&*vmf)&{&&&&&&&unsignedlong&&&&&&&&void&*our_&&&&&&&&offset&=(unsigned&long)vmf-&virtual_address&-&(unsignedlong)vma-&vm_&&/*&计算PAGE_FAULT时的偏移量&*/&&&&&&&if(offset&&=&kernel_space_size)&{&return&-VM_FAULT_NOPAGE;&}&&/*&这次是真的页错误了&*/&&&&&&&our_addr=&kernel_space_addr&+&&/*&得到该偏移量下的内核虚拟地址&*/&&&&&&vmf-&page&=&vmalloc_to_page(our_addr);&/*&将得到的页面告知内核&*/&&&&&&get_page(vmf-&page);&/*&别忘了增加其引用计数&*/&&&&&&&return0;&&}&&&&&static&const&struct&vm_operations_struct&vmops&=&{&&&&&&&&&.fault&&&&&&&&&&&&&&&&&=&vma_fault,&&};&&&&&int&mmap(struct&file&*file,&struct&vm_area_struct*vma)&{&&&&&vma-&vm_ops&=&&&/*&指定vm_ops&*/&&&&&vma-&vm_flags&|=&VM_RESERVED;&/*&声明这片内存区不能交换!&*/&&&&&&return&0;&&}&&
不连续页的另外一个实现:remap_vmalloc_range
这是2.6.18后的内核版本实现的,使用方法也很简单:
[cpp] int&remap_vmalloc_range(structvm_area_struct&*vma,&void&*addr,unsigned&long&pgoff);&&
8.示例程序
在内核驱动程序的初始化阶段,通过ioremap()将物理地址映射到内核虚拟空间;在驱动程序的mmap系统调用中,使用remap_page_range()将该块ROM映射到用户虚拟空间。这样内核空间和用户空间都能访问这段被映射后的虚拟地址。
[cpp] /************mmap_ioremap.c**************/&&#include&&linux/module.h&&&#include&&linux/kernel.h&&&#include&&linux/errno.h&&&#include&&linux/mm.h&&&#include&&linux/wrapper.h&&/*&formem_map_(un)reserve&*/&&#include&&asm/io.h&&/*&for&virt_to_phys&*/&&#include&&linux/slab.h&&/*&for&kmalloc&andkfree&*/&&&&&MODULE_PARM(mem_start,&"i");&&MODULE_PARM(mem_size,&"i");&&&&&static&int&mem_start&=&101,&mem_size&=&10;&&static&char&*reserve_virt_&&static&int&&&&&&int&mmapdrv_open(struct&inode&*inode,&struct&file*file);&&int&mmapdrv_release(struct&inode&*inode,&structfile&*file);&&int&mmapdrv_mmap(struct&file&*file,&structvm_area_struct&*vma);&&&&&static&struct&file_operations&mmapdrv_fops&=&&{&& owner:THIS_MODULE,&&mmap:&mmapdrv_mmap,&&open:&mmapdrv_open,&&release:&mmapdrv_release,&&};&&&&&int&init_module(void)&&{&& if&((major&=register_chrdev(0,&"mmapdrv",&&mmapdrv_fops))&&&0)&& {&&  printk("mmapdrv:unable&to&register&character&device/n");&&  return&(&-&EIO);&& }&& printk("mmapdevice&major&=&%d/n",&major);&&&&& printk("highmemory&physical&address&0x%ldM/n",&virt_to_phys(high_memory)&/1024&/&1024);&&&&& reserve_virt_addr=&ioremap(mem_start&*1024&*&1024,&mem_size&*1024&*&1024);&& printk("reserve_virt_addr=&0x%lx/n",&(unsigned&long)reserve_virt_addr);&& if(reserve_virt_addr)&& {&&  int&i;&&  for&(i&=&0;&i&&mem_size&*1024&*&1024;&i&+=&4)&&  {&&   reserve_virt_addr[i]=&'a';&&   reserve_virt_addr[i+&1]&=&'b';&&   reserve_virt_addr[i+&2]&=&'c';&&   reserve_virt_addr[i+&3]&=&'d';&&  }&& }&& else&& {&&  unregister_chrdev(major,"mmapdrv");&&  return&-&ENODEV;&& }&& return&0;&&}&&&&&/*&remove&the&module&*/&&void&cleanup_module(void)&&{&& if(reserve_virt_addr)&&  iounmap(reserve_virt_addr);&&&&& unregister_chrdev(major,"mmapdrv");&& return&;&&}&&&&&int&mmapdrv_open(struct&inode&*inode,&struct&file*file)&&{&& MOD_INC_USE_COUNT;&& return&(0);&&}&&&&&int&mmapdrv_release(struct&inode&*inode,&structfile&*file)&&{&& MOD_DEC_USE_COUNT;&& return&(0);&&}&&&&&int&mmapdrv_mmap(struct&file&*file,&structvm_area_struct&*vma)&&{&& unsigned&longoffset&=&vma-&vm_pgoff&&&&PAGE_SHIFT;&& unsigned&longsize&=&vma-&vm_end&-&vma-&vm_&&&&& if&(size&&mem_size&*1024&*&1024)&& {&&  printk("sizetoo&big/n");&&  return&(&-ENXIO);&& }&&&&& offset&=&offset&+mem_start&*&1024&*&1024;&&&&& /*&we&do&not&wantto&have&this&area&swapped&out,&lock&it&*/&& vma-&vm_flags|=&VM_LOCKED;&& if(remap_page_range(vma,&vma-&vm_start,&offset,&size,&PAGE_SHARED))&& {&&  printk("remappage&range&failed/n");&&  return&-&ENXIO;&& }&& return&(0);&&}&&
&&&&&& 关于high_memory:
linux内核规定,只映射0-896M物理内存(如果有的话,称为low memory)到内核空间,也就是0xc到0xcM,而且是线性映射,有物理地址0&=x&=896M,就有内核地址0xc0000000+x。
如果物理内存y&896M,则high_memory=0xc0000000+y(是虚拟地址);否则high_memory=0xcM&或者说high_memory不能超过0xcM&
//high_memory =(void *) __va(max_low_pfn * PAGE_SIZE);&
所以内核情景分析上说high_memory是“具体物理内存的上限对应的虚拟地址”。
如果内核空间需要虚拟空间,就在high_memory+8m分配&。源码中留一个8MB的空洞,以及在每次分配虚存区间时也要留下一个页面的空洞,是为了便于捕捉可能的越界访问。
9.内存映射
内存映射并非映射文件内容到内存中,他的最终目的是提供访问某段物理内存的一种途径,其过程是构造访问这段物理内存的对应的页表项。如果在内核空间来映射,是在内核空间(3G以上)构造页表项,来指向相应的物理内存,例如ioremap目标就是把设备内存的物理地址填到内核页表中,推而广之,kmalloc/vmalloc等也可以算是是一种内存映射,说来其实与ioremap目标一样,只不过后者物理介质是系统内存,前者是设备内存。如果在用户空间映射,是在用户进程地址空间(3G以下)来构造页表指向欲访问的物理地址,这个物理地址可能是设备内存,也可能是内核空间分配的内存(kmalloc/vmalloc),却想在用户空间访问。在用户空间来映射,根据页表构造的途径的不同,又有两种途径,一种是物理地址连续的,这样就可以一次搞定(通过remap_page_range),如果物理地址不连续(多个不连续的物理页面),如果不怕麻烦,可以把这些页面的物理地址都一个个找出来,然后在填到页表项中,这算一种不lazy的方法,似乎也很少用。lazy的方法就是通过缺页异常做,这也就是vm_operations_struct中fault的用途所在。&
TA的最新馆藏

我要回帖

更多关于 物流配送软件 的文章

 

随机推荐