本文所涉及到的源代码为 wheelz原创,freas_1990加工整理。转载请标明出处:http://blog.csdn.net/freas_1990/article/details/19653015
偶然在网上偶然翻到一篇 wheelz在多年前写的通过mmap + /dev/mem + 共享内存来实现zero-copy的文章, wheelz在网上已经很久没路面了,本文 简单加批注如下。
模块程序:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mm.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Wheelz");
MODULE_DESCRIPTION("mmap demo");
static unsigned long p = 0;
static int __init init(void) {
//分配共享内存(一个页面)
p = __get_free_pages(GFP_KERNEL, 0);
SetPageReserved(virt_to_page(p));
//这里的地址会被记录到/var/log/message文件里面去,
//用户空间的程序里的读取地址是人工从该文件里面“看”出来,然后再手工添加到代码里去
printk("<1> p = 0x%08x\n", p);
//在共享内存中写上一个字符串
strcpy(p, "Hello world!\n");
return 0;
}
static void __exit fini(void) {
ClearPageReserved(virt_to_page(p));
free_pages(p, 0);
}
module_init(init);
module_exit(fini);
用户态程序:
#include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #define PAGE_SIZE (4*1024) #define PAGE_OFFSET 0xc0000000 #define KERNEL_VIRT_ADDR 0xc5e3c000 //这个地址需要读取/var/log/message查看,每台机器每次运行都需要修改 int main() { char *buf; int fd; unsigned long phy_addr; //这里的/dev/mem是整个物理地址空间的映射 fd=open("/dev/mem",O_RDWR); if(fd == -1) perror("open"); phy_addr=KERNEL_VIRT_ADDR - PAGE_OFFSET; //通过mmap映射物理地址到user space的虚拟地址 //由于/dev/mem映射的是物理地址空间,所以start设置为NULL,这样offset就可以直接设置成物理地址。 buf=mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, phy_addr); if(buf == MAP_FAILED) perror("mmap"); puts(buf);//打印共享内存的内容 munmap(buf,PAGE_SIZE); close(fd); return 0; }Makefile如下:
ifneq ($(KERNELRELEASE),) obj-m:=mod.o else KDIR := /lib/modules/$(shell uname -r)/build all: make -C $(KDIR) M=$(PWD) modules clean: make -C $(KDIR) M=$(PWD) clean endif运行用户态程序的时候,可能会报一个莫名其妙的错误:
mmap: Operation not permitted
segmentation fault
这个问题是内核配置引起的,把以下三个选项设置下:
CONFIG_STRICT_DEVMEM=n CONFIG_X86_PAT=n CONFIG_EXPERT=y最后再附上module的常用命令:
加载名字为mod的模块
#insmod ./mod
查看名为mod的module
#lsmod |grep "mod"
删除名为mod的模块
#lsmod |grep "mod"