陳學松《深入Linux設備驅動程序內核機制》之MMAP內存頁面示例

本例例化一個字符設備,該設備申請一塊內存,file_operations中有mmap的功能,在測試程序test.c中mmap這塊內存,操作這塊用戶內存即可以修改設備內存

驅動代碼 mmap_demo.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/gfp.h>
#include <linux/string.h>
#include <linux/mm_types.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/highmem.h>
#include <asm/io.h>

#include <linux/miscdevice.h>

#define	mmap_printk(args...) do {printk(KERN_ALERT "MMAP_DEMO " args); } while(0)
#define	KSTR_DEF	"hello world from kernel virtual space"
#define	mmap_name	"mmap_demo"

static struct page *pg;
static struct timer_list timer;

static void
timer_func (unsigned long data)
{
  printk ("timer_func:%s\n", (char *) data);
  timer.expires = jiffies + HZ * 10;
  add_timer (&timer);
}

static int
demo_open (struct inode *inode, struct file *filp)
{
  mmap_printk ("mmap_demo device open\n");
  return 0;
}

static int
demo_release (struct inode *inode, struct file *filp)
{
  mmap_printk ("mmap_demo device closed\n");
  return 0;
}

static int
demo_mmap (struct file *filp, struct vm_area_struct *vma)
{
  int err = 0;
  unsigned long start = vma->vm_start;
  unsigned long size = vma->vm_end - vma->vm_start;

  err = remap_pfn_range (vma, start, vma->vm_pgoff, size, vma->vm_page_prot);
  return err;
}

static struct file_operations mmap_fops = {
  .owner = THIS_MODULE,
  .open = demo_open,
  .release = demo_release,
  .mmap = demo_mmap,
};

static struct miscdevice misc = {
  .minor = MISC_DYNAMIC_MINOR,
  .name = mmap_name,
  .fops = &mmap_fops,
};

static int __init
demo_map_init (void)
{
  int ret = 0;

  char *kstr;

  pg = alloc_pages (GFP_HIGHUSER, 0);

  SetPageReserved (pg);

  kstr = (char *) kmap (pg);
  strcpy (kstr, KSTR_DEF);
  printk ("kpa = 0x%lx, kernel string = %s\n", page_to_phys (pg), kstr);

  init_timer (&timer);
  timer.function = timer_func;
  timer.data = (unsigned long) kstr;
  timer.expires = jiffies + HZ * 10;
  add_timer (&timer);

  ret = misc_register (&misc);
  printk ("the mmap miscdevice registered\n");
  return ret;
}

module_init (demo_map_init);

static void
demo_map_exit (void)
{
  del_timer_sync (&timer);

  misc_deregister (&misc);
  printk ("the device misc_mmap deregistered\n");

  kunmap (pg);
  ClearPageReserved (pg);
  __free_pages (pg, 0);
}

module_exit (demo_map_exit);
MODULE_LICENSE ("DUAL BSD/GPL");
MODULE_AUTHOR ("BG2BKK");
Makefile

KERNELDIR=/lib/modules/$(shell uname -r)/build
PWD=$(shell pwd)
obj-m = mmap_demo.o

modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
test: test.c
	gcc $< -o [email protected]

clean:
	rm -rf *.o *~ *.ko* *.order *.symvers *.mod*

測試代碼

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>

#define	MAP_SIZE 4096
#define	USTR_DEF "String changed from the user space"

int main(int argc, char *argv[])
{
	int fd;
	char *pdata;

	if(argc <= 1)
	{
		printf("USAGE: main devfile pamapped\n");
		return 0;
	}

	fd = open(argv[1], O_RDWR | O_NDELAY);
	if(fd  >= 0)
	{
		pdata	= (char *)mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, strtoul(argv[2], 0, 16));
		printf("USERAddr = %p, DATA from kernel %s\n",pdata, pdata);
		printf("Writing a string to the kernel space...\n");
		strcpy(pdata, USTR_DEF);
		printf("Done\n");
		munmap(pdata, MAP_SIZE);
		close(fd);
	}

	return 0;
}

測試過程

make

make test

sudo insmod mmap_demo.ko

再dmesg查看mmap_demo設備中的內存頁面地址,打印輸出爲

kpa = 0xc1f0000, kernel string = (null)
the mmap miscdevice registered
timer_func:hello world from kernel virtual space
timer_func:hello world from kernel virtual space
timer_func:hello world from kernel virtual space
timer_func:hello world from kernel virtual space
timer_func:hello world from kernel virtual space
timer_func:hello world from kernel virtual space
可知地址爲0xc1f0000

然後sudo ./test.o  /dev/mmap_demo 0xc1f0000

然後輸出爲

USERAddr = 0xb7744000, DATA from kernel hello world from kernel virtual space
Writing a string to the kernel space...
Done

dmesg的輸出結果爲

timer_func:hello world from kernel virtual space
timer_func:hello world from kernel virtual space
MMAP_DEMO mmap_demo device open
MMAP_DEMO mmap_demo device closed
timer_func:String changed from the user space
timer_func:String changed from the user space
這段內存中原來的數據
hello world from kernel virtual space  變爲了 
String changed from the user space


這個例子表示了簡單的mmap設備的使用方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章