Avoid memory copying between user space and kernel space
2011-09-01 23:45
405 查看
http://www.linuxforums.org/forum/kernel/158548-avoid-memory-copying-between-user-space-kernel-space.html
1. you allocate memory in the kernel device driver
2. You write a "mmap" function in the device driver
3. This mmap file_ops function will do a remap_pfn when invoked by the user-space application
4. In the application code, call the driver mmap.
5. The driver will now return a pointer to the memory that the driver allocated in kernel space.
Thus, a memory portion is now visible to both the kernel device driver and the user-space application program. The Rubini (LINUX设备驱动程序)book has an example though it is a bit confusing and brief at first sight.
1. you allocate memory in the kernel device driver
2. You write a "mmap" function in the device driver
3. This mmap file_ops function will do a remap_pfn when invoked by the user-space application
4. In the application code, call the driver mmap.
5. The driver will now return a pointer to the memory that the driver allocated in kernel space.
Thus, a memory portion is now visible to both the kernel device driver and the user-space application program. The Rubini (LINUX设备驱动程序)book has an example though it is a bit confusing and brief at first sight.
mmap will establish a mapping between kerneland userspace, We can use it toread data from kernel more quickly. mmap is a function pointer in driver, so we have towrite a driver to realize it, a simplechar device driver is enough. If you don't know how to write a simple char device driver, my code also can help you know it. I descript the process as below: 1. define a mmap function for struct file_operations, which will register as a driver. 2. When userspace call mmap(system call), file_operations->mmap() will be called. 3. file_operations->mmap should call remap_page_range() to map the memory between userspace and kernel space. 4. userspace call mmap actively, mmap return a void pointer. Now If userspace modify the pointer's content, kernel will be modified at the sametime. here is also some link which maybe can help you: 1. has a sample too: http://linux.insigma.com.cn/devbbs/printpage.asp?BoardID=14&ID=100 2. has a userspace sample too: http://www.opengroup.org/onlinepubs/009695399/functions/mmap.html 3. "man mmap" will help you more, such as the difference between MAP_SHAREDand MAP_PRIVATE. 4. Linux Device Driver(ldd2) has some introduce about mmap.(Section 13) Note: 1. We should malloc whole page memory in kernelfor map. 2. A non-regularfile can't be map to write. 3. The size that userspace request to map, will be changed to whole page then sent to kernel. below is code: kernel module: mmap.c Code: #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/slab.h> #include <asm/uaccess.h> #include <linux/wrapper.h> #include <asm/io.h> MODULE_PARM(mmap_major, "i"); MODULE_PARM(mmap_nr_devs, "i"); #define DEVICE "mmap" #define DATASIZE PAGE_SIZE<<3 int mmap_major = 0; int mmap_nr_devs = 1; typedef struct mmap_state { char data[DATASIZE]; unsigned int size; void *handle; unsigned int access_key; struct semaphore sem; }mmap_state; #define TYPE(dev) (MINOR(dev) >>4) #define NUM(dev) (MINOR(dev) &0xf) int mmap_open(struct inode *inode, struct file *filp); ssize_t mmap_read(struct file *filp, char *buf, size_t count, loff_t *f_pos); ssize_t mmap_write(struct file *filp,const char *buf,size_t count,loff_t *f_pos); int mmap_release(struct inode *inode, struct file *filp); static int mmap_mmap(struct file * file, struct vm_area_struct * vma); int mmap_trim(mmap_state *dev); int mmap_init_module(void); void mmap_cleanup_module(void); struct file_operations mmap_fops = { open: mmap_open, read: mmap_read, write: mmap_write, llseek: NULL, ioctl: NULL, release: mmap_release, mmap: mmap_mmap, }; mmap_state *mmap_devices; int mmap_open(struct inode *inode, struct file *filp) { mmap_state *dev; int num = NUM(inode->i_rdev); int type = TYPE(inode->i_rdev); if (!filp->private_data && type) { printk(KERN_WARNING"data is not valid\n"); return 0; } dev = (mmap_state *)filp->private_data; if (!dev) { if (num >= mmap_nr_devs) return -ENODEV; dev = &mmap_devices[num]; filp->private_data = dev; } MOD_INC_USE_COUNT; if ( (filp->f_flags & O_ACCMODE) == O_WRONLY) { if (down_interruptible(&dev->sem)) { MOD_DEC_USE_COUNT; return -ERESTARTSYS; } mmap_trim(dev); up(&dev->sem); } return 0; } static int mmap_mmap(struct file * file, struct vm_area_struct * vma) { struct mmap_state *state = (struct mmap_state *)file->private_data; unsigned long size; int ret = -EINVAL; //printk("mmap_mmap()\n"); if (vma->vm_pgoff != 0) { printk(" vm_pgoff != 0\n"); goto error; } /* Don't try to swap out physical pages..*/ vma->vm_flags|= VM_RESERVED; size = vma->vm_end- vma->vm_start; //printk(" data = [%p]\n", state->data); //printk(" content = [%s]\n", state->data); //printk(" start=[%lu] size=[%lu] end=[%lu]\n", vma->vm_start, size, vma->vm_end); if (size > state->size) goto error; if (remap_page_range( vma->vm_start, virt_to_phys(state->data), size, vma->vm_page_prot)) return -EAGAIN; //printk("mmap_mmap() success\n"); return 0; error: return ret; } int mmap_release(struct inode*inode, struct file *filp) { MOD_DEC_USE_COUNT; return 0; } ssize_t mmap_read(structfile *filp,char *buf,size_t count, loff_t *f_pos) { int ret = 0; mmap_state *dev = filp->private_data; if (down_interruptible(&dev->sem)) return -ERESTARTSYS; if (*f_pos>= dev->size) goto out; if (*f_pos+ count > dev->size) count = dev->size- *f_pos; if (copy_to_user(buf,&dev->data[*f_pos],count)) { ret = -EFAULT; goto out; } *f_pos +=count; ret = count; out: up(&dev->sem); return ret; } ssize_t mmap_write(structfile *filp,const char *buf, size_tcount,loff_t *f_pos) { int ret = 0; mmap_state *dev = filp->private_data; if (down_interruptible(&dev->sem)) return -ERESTARTSYS; if (*f_pos+ count > dev->size) count = dev->size- *f_pos; if (copy_from_user(&dev->data[*f_pos], buf, count)) { ret = -EFAULT; goto out; } *f_pos +=count; ret = count; if (dev->size< *f_pos) dev->size= *f_pos; out: up(&dev->sem); return ret; } int mmap_trim(mmap_state*dev) { memset(dev->data, 0,sizeof(dev->data)); return 0; } int mmap_init_module(void) { int result, i; struct page *page; SET_MODULE_OWNER(&mmap_fops); result = register_chrdev(mmap_major, DEVICE,&mmap_fops); if (result < 0) { printk(KERN_WARNING "mmap:cann't get major %d\n", mmap_major); return result; } if (mmap_major== 0) mmap_major = result; mmap_devices = kmalloc(mmap_nr_devs*sizeof(mmap_state),GFP_KERNEL); if (!mmap_devices) { result = -ENOMEM; goto fail; } memset(mmap_devices, 0, mmap_nr_devs* sizeof(mmap_state)); for (i = 0; i < mmap_nr_devs; i++) { memset(mmap_devices[i].data, 0,sizeof(mmap_devices[i].data)); strcpy(mmap_devices[i].data,"aaa"); mmap_devices[i].size= DATASIZE; /* Note here: if miss it, user space will get NULL */ for (page= virt_to_page(mmap_devices[i].data); page <= virt_to_page(mmap_devices[i].data+ (DATASIZE)); page++) { mem_map_reserve(page); } sema_init(&mmap_devices[i].sem, 1); } EXPORT_NO_SYMBOLS; return 0; fail: mmap_cleanup_module(); return result; } void mmap_cleanup_module(void) { int i; unregister_chrdev(mmap_major, DEVICE); if (mmap_devices) { for (i = 0; i < mmap_nr_devs; i++) mmap_trim(mmap_devices + i); kfree(mmap_devices); } } module_init(mmap_init_module); module_exit(mmap_cleanup_module); MODULE_LICENSE("GPL"); EXPORT_NO_SYMBOLS; Makefile here: Code: KERNELDIR = /usr/src/linux include $(KERNELDIR)/.config CFLAGS = -DEXPORT_SYMTAB-D__KERNEL__ -DMODULE-I$(KERNELDIR)/include-O -Wall ifdef CONFIG_SMP CFLAGS += -D__SMP__ -DSMP endif ifdef CONFIG_MODVERSIONS CFLAGS += -DMODVERSIONS \ -include $(KERNELDIR)/include/linux/modversions.h endif OBJ=mmap.o all:$(OBJ) clean: rm -f *.o userspace: mmap_user.c Code: #include <sys/mman.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> int main() { char *ptr = NULL; int fd = open("/dev/mmap0", O_RDWR); if (fd <= 0) { printf("open fail\n"); return 1; } ptr = mmap(0, 90, PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0); printf("ptr = [%s]\n", ptr); ptr[2] = 'c'; printf("ptr = [%s]\n", ptr); } here is also a script to register a device: Code: #!/bin/sh module="mmap" device="mmap" mode="664" /sbin/insmod ./$module.o $*|| exit 1 major=`cat /proc/devices | awk"\\$2==\"$device\" {print \\$1}"` echo $major rm -f /dev/${device}[0-3] mknod /dev/${device}0 c $major 0 ln -sf ${device}0/dev/${device}
相关文章推荐
- Avoid memory copying between user space and kernel space
- What is the difference between user level threads and kernel level threads?
- Kernel space DMA and User space DMA
- Kernel space DMA and User space DMA
- How to share memory between services and user processes?
- linux i2c access in kernel and user space
- Communication Between User Mode and Kernel Mode 用户模式和内核模式间的通信
- Memory Allocation API In Linux Kernel && Linux Userspace、kmalloc vmalloc Difference、Kernel Large Section Memory Allocation
- What is the difference between user mode and kernel mode, and real mode and protected mode?
- the difference between kernel stack and user stack
- Execution Differences Between Kernel Modules and User Programs
- Modules, User Space and Kernel Space
- kernel space and user space
- [内核]Linux UserSpace和Kernel之间如何联系
- Packet reception between driver and kernel
- MDB: virtual and physical memory map for both kernel and application
- ZZ - Mapping Pointers and Sharing Memory between Processes: Windows CE 5.0 vs. Windows Embedded CE 6.0
- Page Cache, the Affair Between Memory and Files(转自melody_lu123)
- Writing Solaris Device Driver: Mapping Device and Kernel Memory
- [转载]VFS—Kernel Space & User Space