陈学松《深入Linux设备驱动程序内核机制》之MMAP内存页面示例

时间:2022-10-17 15:48:03

本例例化一个字符设备,该设备申请一块内存,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>

#definemmap_printk(args...) do {printk(KERN_ALERT "MMAP_DEMO " args); } while(0)
#defineKSTR_DEF"hello world from kernel virtual space"
#definemmap_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 $@.o

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>

#defineMAP_SIZE 4096
#defineUSTR_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 spacetimer_func:hello world from kernel virtual spacetimer_func:hello world from kernel virtual spacetimer_func:hello world from kernel virtual spacetimer_func:hello world from kernel virtual spacetimer_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设备的使用方法。