Chinaunix
标题:
高手指点mmap实现问题
[打印本页]
作者:
bin_linux96
时间:
2011-12-02 08:52
标题:
高手指点mmap实现问题
自己写了一个虚拟的字符设备/dev/test ,字符设备在内核中为一块内存(用kmalloc),
我想实现的功能是:先通过mmap函数把设备文件映射到用户空间,然后通过strncpy函数向设备写入数据,然后再通过read去读我写入的数据。
我的问题是:在用户空间中如设置mmap的参数:offset = 0;可以正常实现功能,也就是可以用read读出来,而当offset = 1024*4(4k倍数)时不能实现我想要的结果(用read读不出用strncpy写入的数据)。其他file_operations函数功能都可正常实现。
几个函数的原型如下
系统调用mmap : void *mmap(void *start, size_t length, int prot, int flags,int fd, off_t offset);
内核中mmap:int (*mmap)(struct file *filpstruct vm_area_struct *vma) ;
int remap_pfn_range(struct vm_area_struct *vma,unsigned long virt,unsigned long pfn,unsigned long size,pgprot_t prot)
用户空间测试代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/mman.h>
#include<string.h>
#define MAP_SIZE 1024*4*10
#define OFF_SET 1024*4
int main(void )
{
int ret,fd ,new_fd ;
int totalSize = 0 ;
char cArray [512] = "22221111111111,2,3,4,5,6,7,8,9,a,b,c,d,e,f" ;
char output [512] = {0} ;
void * vir_addr = NULL ;
fd = open("/dev/test",O_RDWR);
if( fd<=0 ){
printf("open file error \n");
return -1 ;
}
vir_addr = mmap(NULL,MAP_SIZE,PROT_READ|PROT_WRITE,MAP_SHARED,fd,OFF_SET);
if(NULL==vir_addr){
printf("mmap error !!!\n");
return -1 ;
}
printf("vir_addr = 0x%x \n",vir_addr);
strncpy(vir_addr,cArray,512);
lseek(fd,OFF_SET,SEEK_SET) [color=Red];//当OFF_SET 是4k的倍数时,不管要不要此句都不行。[/color]
int i = 0 ,c;
for(i = 0;i<50;i++){
read(fd,&c,1);
printf("read result = %c\n",c);
}
close(new_fd) ;
return 0;
}
复制代码
内核空间代码:
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/module.h>
//#include<linux/sched.h>
#include<linux/fs.h>
#include<linux/types.h>
#include<linux/kdev_t.h>
#include<linux/cdev.h>
#include<asm/uaccess.h>
#include<linux/slab.h>
#include<linux/mm.h>
#if 1
#define dprintk(fmt,args...) \
printk("Dbg->%s_%d:"fmt,__FUNCTION__,__LINE__,##args)
#else
#define dprintk(fmt,args...)
#endif
#define eprintk(fmt,args...) \
printk("Err->%s_%d:"fmt,__FUNCTION__,__LINE__,##args)
static int gMajor = 0 ;
static int gMinor = 0 ;
module_param(gMajor,int,0);
#define DEVICE "test"
struct cdev * my_cdev = NULL ;
#define KMALLOC_SIZE 1024*80
int test_open(struct inode *inode,struct file *filp)
{
struct cdev * pdev = NULL ;
int major = 0 ;
filp->private_data = kmalloc(KMALLOC_SIZE,GFP_KERNEL) ;
if(filp->private_data==NULL){
printk("test_open error !!!\n");
return -1 ;
}
if(inode->i_cdev)pdev= inode->i_cdev ;
major = MAJOR(pdev->dev) ;
dprintk("major = %d \n",major) ;
return 0;
}
int test_release(struct inode *inode ,struct file *filp)
{
if(filp->private_data)kfree(filp->private_data);
dprintk("\n");
return 0;
}
ssize_t test_read(struct file *filp,char __user *buffer,size_t count,loff_t *offp)
{
int ret = -1 ;
dprintk("the file position *offp = %lld,file pos =%lld\n",(long long)(*offp),(long long)filp->f_pos);
if(count +*offp>KMALLOC_SIZE){
count = KMALLOC_SIZE - *offp ;
}
ret = copy_to_user(buffer,filp->private_data+(*offp),(unsigned long)count);
if(ret != 0){
eprintk("test_read error ret = %d!!!\n",ret);
return -1 ;
}
*offp +=count ;
dprintk("the file position *offp = %lld,file pos =%lld\n",(long long)(*offp),(long long)filp->f_pos);
return count ;
}
ssize_t test_write(struct file *filp,const char __user *buffer,size_t count,loff_t *offp)
{
int ret = -1 ;
if(*offp+count>KMALLOC_SIZE){
printk("the device space is limit\n");
count = KMALLOC_SIZE-*offp ;
}
ret = copy_from_user((char *)filp->private_data+(*offp),buffer,count);
if(ret != 0){
eprintk("write error ret = %d!!!\n",ret);
return ret ;
}
*offp +=count ;
dprintk("the file position *offp = %lld,file pos =%lld\n",(long long)(*offp),(long long)filp->f_pos);
return count ;
}
loff_t test_seek(struct file *filp,loff_t off,int where)
{
switch(where)
{
case 0 :
filp->f_pos = 0 ;
filp->f_pos += off ;
break ;
case 1 :
filp->f_pos += off ;
break ;
case 2 :
filp->f_pos = 0 ;
filp->f_pos += KMALLOC_SIZE ;
default :
break ;
}
return filp->f_pos ;
}
int test_mmap(struct file *filp,struct vm_area_struct *vm)
{
unsigned long phy_addr = __pa(filp->private_data);
unsigned long pfn = (phy_addr>>PAGE_SHIFT) + vm->vm_pgoff [color=Red];//主要在于如果vm_pgoff不为0时则实现不了功能。[/color] dprintk("pfn = %ld,phy_addr = %x pgoff = %d\n",pfn,phy_addr,vm->vm_pgoff);
dprintk("vm->vm_start = 0x%x vm->vm_end = 0x%x size = %d vm->vm_pgoff = %d \n",vm->vm_start,vm->vm_end,\
vm->vm_end - vm->vm_start,vm->vm_pgoff);
if(remap_pfn_range(vm,vm->vm_start,pfn,vm->vm_end-vm->vm_start,vm->vm_page_prot))
return -EAGAIN;
vm->vm_flags |= VM_RESERVED ;
vm->vm_page_prot = pgprot_noncached(vm->vm_page_prot);
return 0;
}
struct file_operations my_ops = {
.owner = THIS_MODULE ,
.open = test_open ,
.release = test_release ,
.mmap = test_mmap ,
.read = test_read ,
.write = test_write ,
.llseek = test_seek ,
} ;
static int __init test_init(void)
{
dev_t dev ;
int ret = -1 ;
dprintk("module init\n");
if(gMajor!=0){
dev = MKDEV(gMajor,gMinor) ;
ret = register_chrdev_region(dev,1,DEVICE) ;
}else{
ret = alloc_chrdev_region(&dev,0,1,DEVICE) ;
gMajor = MAJOR(dev);
}
if(ret != 0){
dprintk("register chrdev dev error \n");
return ret ;
}
dprintk("major = %d \n",gMajor);
my_cdev = cdev_alloc() ;
my_cdev->ops = &my_ops ;
my_cdev->owner = THIS_MODULE ;
cdev_add(my_cdev,dev,1) ;
return 0;
}
static void __exit test_exit(void)
{
dprintk("module exit \n");
cdev_del(my_cdev) ;
unregister_chrdev_region(MKDEV(gMajor,gMinor),1);
}
module_init(test_init) ;
module_exit(test_exit) ;
MODULE_LICENSE("GPL");
MODULE_AUTHOR("BIN");
复制代码
欢迎光临 Chinaunix (http://bbs.chinaunix.net/)
Powered by Discuz! X3.2