Avoid memory copying between user space and kernel space
http://www.linuxforums.org/forum/kernel/158548-avoid-memory-copying-between-user-space-kernel-space.html1. you allocate memory in the ke
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 driver3. 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}
更多推荐
所有评论(0)