Chinaunix首页 | 论坛 | 博客
  • 博客访问: 439166
  • 博文数量: 185
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 681
  • 用 户 组: 普通用户
  • 注册时间: 2011-08-06 21:45
个人简介

为梦而战

文章分类

全部博文(185)

文章存档

2016年(3)

2015年(103)

2014年(79)

我的朋友

分类: LINUX

2015-04-22 15:46:23

 

本文乃fireaxe原创,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,并注明原作者及原链接,严禁用于任何商业用途。
作者:fireaxe_hq@hotmail.com
博客: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空间需要用制定物理地址的方式来进行映射,而一般的内存只要用kmallocmalloc直接申请就可以了。

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传入的paddrEPLD_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发布,可以自由拷贝,转载。但转载请保持文档的完整性,并注明原作者及原链接,严禁用于任何商业用途。
作者:fireaxe_hq@hotmail.com
博客:fireaxe.blog.chinaunix.net 
阅读(717) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~