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) modules moules_install: $(MAKE) -C $(KERSRC) M=$(PWD) modules_install .PHONY: modules modules_install clean clean: -rm -rf *.o *.cmd.* *.ko else modules-objs :=mmap3.o obj-m := mmap3.o endif
测试代码
#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时才会引用缺机制调用内存映射。