全部博文(92)
分类: 嵌入式
2010-04-15 16:44:18
内存与I/O访问
CPU对外设I/O端口地址的编址方式有两种:
1. I/O映射方式(I/O-mapped)
2. 内存映射方式(Memory-mapped)
具体采用哪一种方式取决于CPU的体系结构。
在x86体系中为外设专门实现了与RAM内存地址不同的一个单独的地址空间,也就是I/O映射方式。可以使用outb,inb等函数直接操作这些I/O端口。
在ARM体系中,外设I/O端口具有与内存相同的物理地址,这样的方式是内存映射方式。
我这里主要学习了内存映射方式(Memory-mapped):
外设的I/O内存资源的物理地址是已知的,由硬件的设计决定。但是CPU通常并没有为这些已知的外设I/O内存资源的物理地址预定虚拟地址,驱动程序不能直接通过物理地址访问I/O内存资源,而必须将他们映射到核心虚拟地址空间中(通过也表),然后才能根据映射所得到的核心虚地址范围,通过访问指令访问这些I/O内存资源。Linux在io.h同文件中声明了函数ioremap()用来将I/O内存资源的物理地址映射到核心虚拟地址空间(3GB~4GB)
Void *ioremap(unsigned ong phys_addr, unsigned long size, unsigned long flags);
//物理地址映射到核心虚地址空间
Void iounmap(void *addr)
//取消ioremap所做的地址映射
看一段源码:
static int button_init(void)
{
int ret;
int result; //add by yoyo
gpecon = ioremap(0x56000040, 0x04);//得到相应IO口的虚拟地址,下同
gpedat = ioremap(0x56000044, 0x04);
gpfcon = ioremap(0x56000050, 0x04);
gpfdat = ioremap(0x56000054, 0x04);
gpgcon = ioremap(0x56000060, 0x04);
gpgdat = ioremap(0x56000064, 0x04);
………..
}
static void __exit button_exit(void)
{
disable_irqs();
free_irqs();
iounmap(gpecon); iounmap( gpedat);
iounmap(gpfcon);
iounmap(gpfdat);
iounmap(gpgcon);
iounmap(gpgdat);
……………
}
用户空间内存动态申请:
在用户空间动态申请内存的函数为malloc(),释放函数为free()
Char * function(void)
{
char *p;
p = (char * )malloc(…);
if(p==NULL)
….;
….//一系列针对p的操作
}
在某处调用function(),用完function()中动态申请的内存后将其free,如下:
char *q= function();
….
free(q);
内核空间内存动态申请:
在Linux内核空间申请内存涉及的函数主要包括kmalloc(), __get_free_pages()和vmalloc()
1.Kmalloc()
Void * kmalloc(size_t size, int flags);
Size:分配的块的大小
Flags:分配标志: GFP_KERNEL:含义是在内核空间的进程中申请内存。Kmalloc()的底层依赖__get_free_pages()实现,使用GFP_KERNEL标志申请内存时,若暂时不能满足,则进程会睡眠等待页,即会引起阻塞,因此不能在中断上下文或持有自旋锁的时候使用GFP_KERNEL申请内存。
GFP_ATOMIC:申请内存时,若不存在空闲页,则不等待,直接返回,可用在中断处理函数,tasklet和内核定时器等进程上下文中
2.__get_free_pages()
__get_free_pages()系列函数/宏是kmalloc()实现的基础。
__get_free_pages(unsigned int flags, unsigned int order);
该函数可分配多个页并返回分配内存的首地址,分配的页数为2^order
释放函数为:
Void free_page()或void free_pages()
3.Vmalloc()
l Vmalloc()远远大于__get_free_pages()的开销,为了完成vmalloc(),新的页表需要被建立。因此,只是调用vmalloc()来分配少量的内存(如1页)是不妥的。
l Vmalloc()不能用于原子上下文,因为它的内部实现使用了标志为GFP_KERNEL的kmalloc()。
l 使用vmalloc()函数的一个例子函数是creat_module()系统调用,它利用vmalloc()函数来获取被创建模块需要的内存空间。