Linux系统开发专栏

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

“小王,不瞒你说,我现在是悲喜交加啊,悲的是:这最后一章,我讲的是胆颤心惊(以前自己都没学好,现在也算还账了),喜的是每讲一张,我知道离结束就近了一点,赶快把这个东西过掉,进入下一环节,那又是我牛皮吹破天的时代了”看着小王期盼和怀疑的眼神,我,昔日的风采也不见了。

“没事的,小涛哥,其实说真的,不是我安慰你哈,从开始我什么都不懂,到现在我也算个入门级的高手了,都是你一手带过来的,我已经对你推崇备至了,你就放心吧,会的你尽情教,不会的,你慢慢说,我都听你的”小王善解人意的说。

“嗯,你真好,看来我没看错你,那好,继续课程”。。。

  一般情况下,用户空间是不可能也不应该直接访问设备的,但是设备驱动程序可实现mmap()函数,这个函数可使得用户空间能直接访问设备的物理地址。实际上,mmap()S实现了这样的一个映射过程,它将用户空间的一段内存与设备内存关联,当用户访问用户空间的这段地址范围时,实际上会转化为对设备的访问。

  mmp()必须以PAGE_SIZE为单位进行映射,实际上,内存只能以页为单位进行映射,若要映射非PAGE_SIZE整数倍的地址范围,要先进行页对齐,强行以PAGE

_SIZE的倍数大小进行映射。驱动中mmp()函数原型如下:int (*mmp)(struct file *, struct vm_area_struct *);它实现的机制是建立页表,并填充VMA结构体中

vm_operations_struct指针,vm_area_struct用于描述一个虚拟内存区域。结构体如下:

struct vm_area_struct {
    struct mm_struct * vm_mm; //所处的地址空间
    unsigned long vm_start;//开始虚拟地址
    unsigned long vm_end;//结束虚拟地址

    struct vm_area_struct *vm_next;

    pgprot_t vm_page_prot;  //访问权限
    unsigned long vm_flags; //标志
     ...
    struct vm_operations_struct * vm_ops;  //操作VMA的函数集指针
    unsigned long vm_pgoff; //偏移(页帧号)
    struct file * vm_file;
    void * vm_private_data;
    ...
};

其中vm_ops成员指向这个VMA的操作集,结构体定义如下:

struct vm_operations_struct {
   void (*open)(struct vm_area_struct * area);
   void (*close)(struct vm_area_struct * area);
   struct page * (*nopage)(struct vm_area_struct * area, unsigned long address, int *type);
   int (*populate)(struct vm_area_struct * area, unsigned long address, unsigned long len, pgprot_t prot, unsigned long pgoff, int nonblock);
    ...
};
在内核生成一个VMA后,它就会调用该VMA的open()函数。
  而驱动中的mmap()函数将在用户进行mmap()系统调用时最终被调用,当用户调用mmap()时候内核会进行如下处理:
  1)在进程的虚拟空间查找一块VMA.
  2)将这块VMA进行映射。
  3)如果设备驱动程序或文件系统的file_operations定义了mmap()操作,则调用它。
  4)将这个VMA插入到进程的VMA链表中。
file_operations中mmap()函数的第一个参数就是步骤1中找的VMA.由mmap()系统调用映射的内存可由munmap()解除映射。这个函数原型如下:
int munmap(caddr_t addr, size_t len);
  但是,需要注意的是:当用户进行mmap()系统调用后,尽管VMA在设备驱动文件操作结构体的mmap()被调用前就已经产生,内核却不会调用VMA的open
函数,通常需要在驱动的mmap()函数中先上调用vma->vm_ops->open().为了说明问题,给出一个vm_operations_struct操作范例:
static int xxx_mmp(struct file *filp, struct vm_area_struct *vma)
{
    if(remap_pfn_range(vma, vma->vm_start, vm->vm_pgoff, vma->vm_end - vma->start, vma->vm_page_prot))   //建立页表
     return - EAGAIN;
    vma->vm_ops = &xxx_remap_vm_ops;
    xxx_vma_open(vma);
    return 0;
}
void xxx_vma_open(struct vm_area_struct *vma) //VMA打开函数
{
  ...
  printk(KERN_NOTICE "xxx VMA open, virt %lx, phys %1x\n",vma->vm_start, vma->vm_pgoff 《PAGE_SHIFT);
}
void xxx_vma_close(struct vm_area_struct *vma)  //VMA关闭函数
{
  ...
  printk(KERN_NOTICE "xxx VMA close. \n");
}
static struct vm_operation_struct xxx_remap_vm_ops = //VM操作结构体
{
   .open=xxx_vma_open,
   .close=xxx_vma_close,
   ...
}
在这段代码中调用的remap_pfn_range()创建页表。我们前边说过在内核空间用kmalloc申请内存,这部分内存如果要映射到用户空间可以通过mem_map
_reserve()调用设置为保留后进行,具体怎么操作,咱们下集继续。
 
posted on 2010-10-26 15:45  ☆&寒 烟☆  阅读(5950)  评论(2编辑  收藏  举报