内存映射nopage()

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时才会引用缺机制调用内存映射。
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
在 Linux 内核中,驱动程序中的 mmap 接口用于将设备内存映射到用户空间中,让用户空间程序可以直接访问设备内存。其中 nopage 是 mmap 接口中的一个函数指针,它用于处理缺页异常。 当用户空间程序访问设备内存时,如果这段内存还没有被映射到物理内存中,就会触发缺页异常。这时,内核会调用 mmap 接口中的 nopage 函数来处理这个异常。nopage 函数的作用是将设备内存的数据读取到物理内存中,然后返回该页的物理地址,让用户空间程序可以继续访问。 以下是一个简单的 nopage 函数的使用示例: ``` static int mydrv_mmap(struct file *filp, struct vm_area_struct *vma) { int ret = 0; struct mydrv_dev *dev = filp->private_data; unsigned long pfn; unsigned long start = vma->vm_start; unsigned long size = vma->vm_end - vma->vm_start; /* 确保映射的地址范围合法 */ if (start < dev->mem_start || start + size > dev->mem_end) { return -EINVAL; } /* 将用户空间的地址转换为物理地址 */ pfn = virt_to_phys((void *)dev->mem_start) >> PAGE_SHIFT; ret = remap_pfn_range(vma, start, pfn, size, vma->vm_page_prot); /* 设置 nopage 函数 */ vma->vm_ops = &mydrv_vm_ops; return ret; } static struct vm_operations_struct mydrv_vm_ops = { .nopage = mydrv_nopage, }; static int mydrv_nopage(struct vm_area_struct *vma, struct vm_fault *vmf) { struct mydrv_dev *dev = vma->vm_private_data; unsigned long phys_addr; void *page; /* 计算物理地址 */ phys_addr = (unsigned long)dev->mem_start + (vmf->pgoff << PAGE_SHIFT); /* 读取设备内存数据到物理内存中 */ page = virt_to_page(phys_to_virt(phys_addr)); if (!page) { return VM_FAULT_SIGBUS; } get_page(page); vmf->page = page; return 0; } ``` 在上述示例中,mydrv_nopage 函数用于处理缺页异常。它首先计算出设备内存的物理地址,然后将该页的数据读取到物理内存中,并返回该页的物理地址。这样,用户空间程序就可以访问这个设备内存页了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值