Linux地址對映(3)--地址轉換
本文乃fireaxe原創,使用GPL釋出,可以自由拷貝,轉載。但轉載請保持文件的完整性,並註明原作者及原連結,嚴禁用於任何商業用途。 作者:[email protected]
部落格:fireaxe.blog.chinaunix.net
三、地址轉換
上圖揭示了程序空間、核心空間與實體地址之間的轉換關係。
在linux中,實體地址用page結構 表示,實體記憶體在初始化時已經生成了page結構管理,其他地址空間則需要生成page再進行管理(ioremap)。實體地址可以被對映到核心空間或程序空間,也可以從核心空間或程序使用者空間解除實體地址(page)。
所有轉換中,只有mmap可以在程序中使用,其他都是核心函式。即使使用mmap,其內部也是靠核心中使用remap_pfn_range實現的。所有地址空間轉換都在核心中實現。
1. 程序空間與核心空間
copy_from_user
copy_to_user
這兩個函式不算是單純的地址轉換,他們完成的是程序空間與地址空間的資料拷貝,使用起來非常簡單。
static ssize_t led_read(struct file *f, __user char *p, size_t size, loff_t *off)
{
if (copy_to_user(p, dev.mem, size))
{
printk(KERN_ALERT”led: led_read Err.\n”);
return -1;
}
printk(KERN_ALERT”led: led_read Ok: off = %d.\n”, off);
return 0;
}
static ssize_t led_write(struct file *f, __user char *p, size_t size, loff_t *off)
{
if (copy_from_user(dev.mem, p, size))
{
printk(KERN_ALERT”led: led_ write Err.\n”);
return -1;
}
printk(KERN_ALERT”led: led_ write Ok: off = %d.\n”, off);
return 0;
}
2. 程序空間與實體地址
get_user_pages
該函式完成程序地址到實體地址的轉換,要注意的是程序地址不一定是也對其的,但得到的實體地址是以page的形式給出,4k對齊,使用時要記得新增偏移量。下面是例程,。其中的current很有用,用於表示呼叫核心函式的程序,是一個task_struct結構體,其中的mm為該程序的記憶體管理結構,mm中的vmalist管理者程序vma連結串列。
static ssize_t led_read(struct file *f, __user char *p, size_t size, loff_t *off)
{
struct page *pg=NULL;
char *addr;
int loop, ret;
down_read(¤t->mm->mmap_sem);
ret = get_user_pages(current, current->mm, p, 1, 0, 1, &pg, NULL);
up_read(¤t->mm->mmap_sem);
addr = (char*)kmap(pg);
if ((PAGE_SIZE-(unsigned long)p & 0xfff) < 8)
{
printk(KERN_ALERT”led: led_read size too small);
return 0;
}
memcpy(addr + ((unsigned long)p & 0xfff), dev.mem, 8);
kunmap(pg);
printk(KERN_ALERT”led: led_read Ok: off = %d.\n”, off);
return 0;
}
remap_pfn_range
mmap
該函式完成實體地址到程序空間的對映,每個程序的地址空間用vma表示,所以就是把實體地址對映入vma。這裡對映的實體地址一般都不會是記憶體,只有在linux管理之外的Device空間需要用制定實體地址的方式來進行對映,而一般的記憶體只要用kmalloc或malloc直接申請就可以了。
mmap是使用者空間需要直接對映實體地址時呼叫的系統呼叫,其內部一般都通過remap_pfn_range實現。
程序中呼叫:
caddr_t addr = mmap(NULL, PAGE_SIZE, PROT_WRITE, MAP_SHARED, f, paddr & PAGE_MASK);
核心中實現:
static int led_mmap(struct file *f, struct vm_area_struct *vma)
{
if (remap_pfn_range(vma, vma->start,
(EPLD_BASE_ADDR + vma->vm_pgoff) >> PAGE_SHIFT,
vma->vm_end – vma->vm_start, vma->vm_page_prot))
{
printk(KERN_ALERT”led: led_mmap error.\n”);
return –EAGAIN;
}
return 0;
}
使用者呼叫mmap後,在進入led_mmap之前,就已經為在程序空間申請號了程序地址空間,會存到一個新的vma結構體中,如果mmap第一個入參為NULL,則這塊地址空間的起始地址有linux 自動分配,長度為第二個引數。led_mmap的入參vma就是之前分配的vma。
vm_pgoff 是前面mmap傳入的paddr。EPLD_BASE_ADDR是裝置基址如果為0,則vm_pgoff就是實際的實體地址。
3. 核心空間與實體地址
kmap
kmap實現實體記憶體到核心地址空間的對映,實體記憶體地址可以是低端記憶體區,也可以是高階記憶體區,如果是低端,作用與page_address相同。
ioremap
ioremap實現實體地址到核心空間的對映,所謂的實體地址一般是指非實體記憶體的地址空間,也就是不在linux管理下的實體地址空間。它可以把這一段對映到核心空間中的非線性空間。
page_address
簡單的地址轉換,只適用於線性區,實現page到核心地址空間的轉換。
phy_to_virt
virt_to_page
virt_to_phy
幾個簡單的地址轉化逆函式,只是用於線性區。
本文乃fireaxe原創,使用GPL釋出,可以自由拷貝,轉載。但轉載請保持文件的完整性,並註明原作者及原連結,嚴禁用於任何商業用途。 作者:[email protected]
部落格:fireaxe.blog.chinaunix.net