1. 程式人生 > >Linux地址對映(3)--地址轉換

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