您的位置:首页 > 运维架构

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.

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}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: