内存映射nopage()

来源:互联网 发布:淘宝主图尺寸高度 编辑:程序博客网 时间:2024/05/01 20:00
struct vm_operations_struct
①在2.6.23之前struct vm_operations_struct中只有
struct page * (*nopage)(struct vm_area_struct * area, unsigned long address, int *type)
从2.6.24后在struct vm_operations_struct中的出现
int (*fault)(struct vm_area_struct *vma, struct vm_fault *vmf)
并在一段时间里,内核同时支持这两个函数,但后面的内核版本取消了
struct page * (*nopage)(struct vm_area_struct * area, unsigned long address, int *type)
在内核2.6.33.20中就取消了该函数
#include <linux/init.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/fs.h>#include <linux/types.h>#include <linux/errno.h>#include <linux/fcntl.h>#include <linux/vmalloc.h>#include <linux/uaccess.h>#include <linux/io.h>#include <asm/page.h>#include <linux/mm.h>#define MMAPNOPAGE_DEV_NAME "mmapnopage"#define MMAPNOPAGE_DEV_MAJOR 240#define SHARE_MEM_PAGE_COUNT 4#define SHARE_MEM_SIZE (PAGE_SIZE*SHARE_MEM_PAGE_COUNT)char *share_memory=NULL;int mmapnopage_vm_fault(struct vm_area_struct *vma,struct vm_fault *vmf){struct page *page;unsigned long offset;void *page_ptr;printk("\n");printk("%-25s %08x\n","1)vma->flags",vmf->flags);printk("%-25s %08x\n","2)vmf->pgoff",vmf->pgoff);printk("%-25s %08x\n","3)vmf->virtual_address",vmf->virtual_address);printk("%-25s %08x\n","4)vma->vm_start",vma->vm_start);printk("%-25s %08x\n","5)vma->vm_end",vma->vm_end);printk("%-25s %08x\n","6)vma->vm_pgoff",vma->vm_pgoff);/*printk("%-25s %d\n","7)PAGE_SHIFT",PAGE_SHIFT);*/page_ptr=NULL;if((NULL==vma)||(NULL==share_memory)){printk("return VM_FAULT_SIGBUS!\n");return VM_FAULT_SIGBUS;}offset=vmf->virtual_address-vma->vm_start;if(offset>=SHARE_MEM_SIZE){printk("return VM_FAULT_SIGBUS!");return VM_FAULT_SIGBUS;}page_ptr=share_memory+offset;page=vmalloc_to_page(page_ptr);get_page(page);vmf->page=page;return 0;}struct vm_operations_struct mmapnopage_vm_ops={.fault=mmapnopage_vm_fault,};int mmapnopage_mmap(struct file *filp,struct vm_area_struct *vma){vma->vm_flags |= VM_RESERVED;vma->vm_ops=&mmapnopage_vm_ops;return 0;}struct file_operations mmapnopage_fops={.owner=THIS_MODULE,.mmap=mmapnopage_mmap,};int mmapnopage_init(void){int lp;int result;result=register_chrdev(MMAPNOPAGE_DEV_MAJOR,MMAPNOPAGE_DEV_NAME,&mmapnopage_fops);if(result<0){return result;}share_memory=vmalloc(SHARE_MEM_SIZE);for(lp=0;lp<SHARE_MEM_PAGE_COUNT;lp++){sprintf(share_memory+PAGE_SIZE*lp,"TEST %d",lp);}return 0;}void mmapnopage_exit(void){if(share_memory!=NULL){vfree(share_memory);}unregister_chrdev(MMAPNOPAGE_DEV_MAJOR,MMAPNOPAGE_DEV_NAME);}module_init(mmapnopage_init);module_exit(mmapnopage_exit);MODULE_LICENSE("Dual BSD/GPL");

Makefile

ifeq ($(KERNELRELEASE),)PWD :=$(shell pwd)KERSRC := /lib/modules/$(shell uname -r)/build/modules:$(MAKE) -C $(KERSRC) M=$(PWD) modulesmoules_install:$(MAKE) -C $(KERSRC) M=$(PWD) modules_install.PHONY: modules modules_install cleanclean:-rm -rf *.o *.cmd.* *.koelsemodules-objs :=mmap3.oobj-m := mmap3.oendif

测试代码

#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/ioctl.h>#include <fcntl.h>#include <unistd.h>#include <sys/mman.h>#define DEVICE_FILENAME "/dev/mmapnopage"#define SHARE_MEM_PAGE_COUNT 5#define SHARE_MEM_SIZE (4096*SHARE_MEM_PAGE_COUNT)int main(){int dev;int loop;char *ptrdata;dev=open(DEVICE_FILENAME,O_RDWR|O_NDELAY);if(dev < 0)printf("can't open mmapnopage\n");    if(dev>=0){printf("open file success!\n");ptrdata=(char*)mmap(0,SHARE_MEM_SIZE,PROT_READ|PROT_WRITE,MAP_SHARED,dev,0);for(loop=0;loop<SHARE_MEM_PAGE_COUNT;loop++){printf("[%-10s----%x]\n",ptrdata+4096*loop,ptrdata+4096*loop);}munmap(ptrdata,SHARE_MEM_SIZE);close(dev);}return 0;}

安装模块之后需要手动创设备节点:mknod /dev/mmapnopage c 240 0

运行测试程充打印:
open file success!
[TEST 0    ----b7794000]
[TEST 1    ----b7795000]
[TEST 2    ----b7796000]
[TEST 3    ----b7797000]
用dmesg查看内核打印:
[ 3409.982684] 1)vma->flags              00000028
[ 3409.982703] 2)vmf->pgoff              00000000
[ 3409.982722] 3)vmf->virtual_address    b7794000
[ 3409.982740] 4)vma->vm_start           b7794000
[ 3409.982758] 5)vma->vm_end             b7799000
[ 3409.982776] 6)vma->vm_pgoff           00000000
[ 3409.983817] 
[ 3409.983820] 1)vma->flags              00000028
[ 3409.983822] 2)vmf->pgoff              00000001
[ 3409.983824] 3)vmf->virtual_address    b7795000
[ 3409.983825] 4)vma->vm_start           b7794000
[ 3409.983827] 5)vma->vm_end             b7799000
[ 3409.983828] 6)vma->vm_pgoff           00000000
[ 3409.984174] 
[ 3409.984177] 1)vma->flags              00000028
[ 3409.984179] 2)vmf->pgoff              00000002
[ 3409.984180] 3)vmf->virtual_address    b7796000
[ 3409.984182] 4)vma->vm_start           b7794000
[ 3409.984183] 5)vma->vm_end             b7799000
[ 3409.984185] 6)vma->vm_pgoff           00000000
[ 3409.984515] 
[ 3409.984518] 1)vma->flags              00000028
[ 3409.984520] 2)vmf->pgoff              00000003
[ 3409.984521] 3)vmf->virtual_address    b7797000
[ 3409.984523] 4)vma->vm_start           b7794000
[ 3409.984524] 5)vma->vm_end             b7799000
[ 3409.984526] 6)vma->vm_pgoff           00000000
[ 3409.984878] 
[ 3409.984881] 1)vma->flags              00000028
[ 3409.984882] 2)vmf->pgoff              00000004
[ 3409.984884] 3)vmf->virtual_address    b7798000
[ 3409.984886] 4)vma->vm_start           b7794000
[ 3409.984887] 5)vma->vm_end             b7799000
[ 3409.984889] 6)vma->vm_pgoff           00000000
[ 3409.984906] return VM_FAULT_SIGBUS!
会在最后一看到映射的内存超出在内核里申请的总量,从而导致请求失败,
同时还注意到用户向内核映射时是以页为单位完成的
如果注释掉测试代码中的for循环会发现在内核中并没有进行映射操,说明只有在操作ptrdata时才会引用缺机制调用内存映射。
原创粉丝点击