爱迟到 发表于 2011-04-12 12:05

kmalloc申请的内存映射到用户态的问题

我最近写一个内存映射的模块,上网查了些资料,自己也写了测试程序,可是映射出现问题:#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/kthread.h>
#include <asm/uaccess.h>
#include <linux/proc_fs.h>
#include <linux/err.h>
#include <linux/mm.h>
#include <asm/page.h>

MODULE_AUTHOR("cwj@126.com");
MODULE_DESCRIPTION("KENEL_MAP.DEMO");
MODULE_LICENSE("GPL");

#define MEM_SHARE_DIR "mem_share_dir"
#define MEM_SHARE_ADDR "mem_share_addr"
#define MEM_SHARE_SIZE "mem_share_size"
#define MEM_SIZE 100
#define PARM_SIZE 32

unsigned long addr;
char addr_py;
char size_py;

unsigned long addr_cnt_len;
int i;

static struct task_struct *thread_map;
static struct proc_dir_entry *mem_share_dir;
static struct proc_dir_entry *mem_share_addr;
static struct proc_dir_entry *mem_share_size;
static unsigned char *p1, *p2, *p3;

long atol(const char *str)
{
        unsigned int index;
        unsigned int num;
        int ret;
       
        ret = 0;
        index = 1;
        num = 0;
       
        //计算字符串的长度(strlen的实现,字符串中只包含数字字符)
        while(*str)
        {
                if(*str < '0' || *str > '9') //判断字符是否是数字
                {
                        return -1;
                }
                str++;
                num++;
        }
       
        //转换字符到相应的数字,这里最好的数据结构描述应该是栈
        while(str--, num--)
        {
                ret += index * (*str - '0');
                index *= 10;
        }
       
        return ret;
}

static int proc_read_addr(char *page, char **start, off_t off, int count, int *eof, void *data)
{
        int ret;
       
        ret = 0;
       
        if(off > 0)
        {
                printk("copy has finished !\n");
                return ret;
        }
        if(addr == 0)
        {
                return ret;
    }
        sprintf(page, "%lu", addr);
        printk("page =%s, addr = %lu\n", page, addr);
        ret = PARM_SIZE;
       
        return ret;
}

static int proc_write_addr(struct file *fp, const char *buf, unsigned long count, void *data)
{
        int ret;
        memset(addr_py, 0, PARM_SIZE);
       
        ret = count;
       
        if(count > PARM_SIZE && count == 0)
        {
                printk("buf fault!\n");
                return 0;
        }

        if(copy_from_user(addr_py, buf, count))
        {
                printk("copy_from_user fault!\n");
                return -1;
        }
               
        return ret;
}

static int proc_read_size(char *page, char **start, off_t off, int count, int *eof, void *data)
{
        int ret;
       
        ret = 0;
       
        if(off > 0)
        {
                printk("copy has finished !\n");
                return ret;
        }
       
        if(addr_cnt_len == 0)
        {
                return 0;
        }
       
        sprintf(page, "%lu", addr_cnt_len);
        ret = PARM_SIZE;
       
        return ret;
}

static int proc_write_size(struct file *fp, const char *buf, unsigned long count, void *data)
{
        int ret;
        memset(size_py, 0, PARM_SIZE);
       
        ret = count;
       
        if(count > PARM_SIZE && count == 0)
        {
                printk("buf fault!\n");
                return 0;
        }
       
        if(copy_from_user(size_py, buf, count))
        {
                printk("copy_from_user fault!\n");
                return -1;
        }
       
        addr_cnt_len = atol(size_py);
       
        return ret;
}

static int thread_map_func(void *data)
{
        if(kthread_should_stop())
                return 0;

        while(p1 || p2 || p3)
        {
                set_current_state(TASK_UNINTERRUPTIBLE);
               
                if(kthread_should_stop())
                        break;
               
                if(i == 3 && addr_cnt_len == 0)
                {
                        break;
                }
               
                if(addr_cnt_len != 0)
                        goto __next;
               
                if(p1 && i == 0)
                {
                        SetPageReserved(virt_to_page(p1));
                        addr = __pa(p1);
                        addr_cnt_len = 27;
                        i++;
                }
                else if(p2 && i == 1)
                {
                        SetPageReserved(virt_to_page(p2));
                        addr = __pa(p2);
                        addr_cnt_len = 27;
                        i++;
                }
                else if(p3 && i == 2)
                {
                        SetPageReserved(virt_to_page(p3));
                        addr = __pa(p3);
                        addr_cnt_len = 27;
                        i++;
                }
                printk("py = %lu\n", addr);
        __next:
                schedule_timeout(50 * HZ);
        }
        return 0;
}

static int __init init_kernel_map(void)
{
        thread_map = NULL;
        mem_share_dir = NULL;
        mem_share_addr = NULL;
        mem_share_size = NULL;
       
        mem_share_dir = proc_mkdir(MEM_SHARE_DIR, NULL);
        if(mem_share_dir == NULL)
        {
                printk("proc create fault!\n");
                return 0;
        }
        mem_share_addr = create_proc_entry(MEM_SHARE_ADDR, 0644, mem_share_dir);
        if(mem_share_addr == NULL)
        {
                remove_proc_entry(MEM_SHARE_DIR, NULL);
                printk("mem_share_addr fault!\n");
                return 0;
        }
        mem_share_addr->read_proc = proc_read_addr;
        mem_share_addr->write_proc = proc_write_addr;
        mem_share_addr->uid = 0;
        mem_share_addr->gid = 0;
       
        mem_share_size = create_proc_entry(MEM_SHARE_SIZE, 0644, mem_share_dir);
        if(mem_share_size == NULL)
        {
                remove_proc_entry(MEM_SHARE_ADDR, mem_share_dir);
                remove_proc_entry(MEM_SHARE_DIR, NULL);
                printk("mem_share_size fault!\n");
                return 0;
        }
        mem_share_size->read_proc = proc_read_size;
        mem_share_size->write_proc = proc_write_size;
        mem_share_size->uid = 0;
        mem_share_size->gid = 0;
        thread_map = kthread_create(thread_map_func, NULL, "kernel_mem_share");
        if(IS_ERR(thread_map))
        {
                printk("thread create fault!\n");
                return 0;
        }
       
        p1 = kmalloc(MEM_SIZE, GFP_KERNEL);
        p2 = kmalloc(MEM_SIZE, GFP_KERNEL);
        p3 = kmalloc(MEM_SIZE, GFP_KERNEL);
        memset(p1, 0, MEM_SIZE);
        memset(p2, 0, MEM_SIZE);
        memset(p3, 0, MEM_SIZE);
       
        memcpy(p1, "111111111111111111111111111", 27);
        memcpy(p2, "222222222222222222222222222", 27);
        memcpy(p3, "333333333333333333333333333", 27);
       
        wake_up_process(thread_map);
       
        return 0;
}

static void __exit exit_kernel_map(void)
{
        if(p1)
        {
                ClearPageReserved(virt_to_page(p1));
                kfree(p1);
                p1 = NULL;
        }
        if(p2)
        {
                ClearPageReserved(virt_to_page(p2));
                kfree(p2);
                p2 = NULL;
        }
        if(p3)
        {
                ClearPageReserved(virt_to_page(p3));
                kfree(p3);
                p3 = NULL;
        }
       
        remove_proc_entry(MEM_SHARE_ADDR, mem_share_dir);
        remove_proc_entry(MEM_SHARE_SIZE, mem_share_dir);
        remove_proc_entry(MEM_SHARE_DIR, NULL);
       
        if(thread_map)
        {
                kthread_stop(thread_map);
                thread_map = NULL;
        }
}

module_init(init_kernel_map);
module_exit(exit_kernel_map);
用户态:
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>

#define MEM_SIZE 100
#define MEM_ADDR_SIZE 32
#define PAGE_SIZE 4096

unsigned long addr;
unsigned long addr_cnt_size;
char _addr;
char addr_cnt_len;
char _buf;
char *map_addr;

int get_mem(int fd, unsigned long _addr, char *buf, unsigned long size)
{
        unsigned long size_cpy;
       
        size_cpy = size;
       
        if(size % PAGE_SIZE)
                size = (size / PAGE_SIZE + 1) << 12;
        else
                size = (size / PAGE_SIZE) << 12;
               
        map_addr =(char *)mmap(0, size , PROT_READ | PROT_WRITE, MAP_SHARED, fd, _addr);
        perror("mmap");
        if(!map_addr)
        {
                return 0;
        }
        memcpy(buf, map_addr, size_cpy);
        munmap(map_addr, size);
        return 1;
}

int main(int argc, char *argv[])
{
        int fd;
        int fd_addr;
        int fd_size;
        int i;
       
        i = 0;
        fd = open("/dev/mem", O_RDWR);
        fd_addr = open("/proc/mem_share_dir/mem_share_addr", O_RDWR);
        fd_size = open("/proc/mem_share_dir/mem_share_size", O_RDWR);
       
        while(1)
        {
                if(i == 0 || !addr_cnt_size){
                memset(_addr, 0, MEM_ADDR_SIZE);
                map_addr = NULL;
                read(fd_addr, _addr, MEM_ADDR_SIZE);
                printf("%s\n", _addr);
                addr = atol(_addr);
                if(addr == 0)
                        continue;
                printf("addr = %u[%8lx]\n", addr);
               
                memset(addr_cnt_len, 0, MEM_ADDR_SIZE);
                read(fd_size, addr_cnt_len, MEM_ADDR_SIZE);
                addr_cnt_size = atol(addr_cnt_len);
                printf("size = %u[%8lx]\n", addr_cnt_size);
               
                if(get_mem(fd, addr, _buf, addr_cnt_size))
                {
                        i++;
                        printf("mem_cnt = %s\n", _buf);
                }
        }
                sleep(100);
               
                if(i == 3)
                {
                        break;
                }
               
        }
       
        close(fd_addr);
        close(fd_size);
        close(fd);
}上面是小弟写的用户态与内核态的例子,在运行该例子的时候,内核地址取的都对但是用户态映射不成功。
用户态程序运行后会出现如下的错误:189023744
addr = 189023744[ b444600]
size = 27[      1b]
mmap: Invalid argument
Segmentation fault (core dumped)
kmalloc既然在实现上是调用的_get_free_pages()实现的,并且分配的是地址是连续的物理地址,大小也肯定是页的整数倍,我的程序理论上应该是可行的,请大家多多讨论,帮助小弟解决下,谢了啊!!

爱迟到 发表于 2011-04-12 13:09

补充下顺便顶下,网上大部分都是那种字符设备映射的例子,我不想那样做,这样做怎么会出错呢?大家给点意见啊

marsbible 发表于 2011-04-15 14:03

内核是不是不支持dev/mem映射到用户态了。貌似需要打开开关编译才行。

looklook155 发表于 2011-04-20 15:38

初学 者路 过.
你是 想要 在 上层 进行 映社 吗?
那 可 能 不行 .因 为 你 从 kernel 那到 的 那 个是 kernel space address, 上层 的function 是 没 法用 的 ,也 就是 那 个mmap会 出 错 .

说 错 勿 怪 ,偶 是 刚 从 win 投诚过来 的.:P

chenrvmldd 发表于 2011-04-21 20:05

回复 1# 爱迟到


    map_addr =(char *)mmap(0, size , PROT_READ | PROT_WRITE, MAP_SHARED, fd, _addr);
    兄弟,你把这个函数的PROT_WRITE这个选项去掉,用户权限级别不能写内核映射出来的内存,你试试

爱迟到 发表于 2011-04-22 14:14

谢谢,我试下啊

chenrvmldd 发表于 2011-04-22 15:39

回复 6# 爱迟到


    试完了之后告诉我一声

ymxlou 发表于 2011-05-03 16:04

回复爱迟到


    map_addr =(char *)mmap(0, size , PROT_READ | PROT_WRITE, MAP_SHARED, fd, _add ...
chenrvmldd 发表于 2011-04-21 20:05 http://bbs.chinaunix.net/images/common/back.gif


    写权限能不能用,关键看driver里面的mmap是怎么实现的,支不支持。

ymxlou 发表于 2011-05-03 16:08

要映射到用户空间,应该用remap_vmalloc_range这样的函数,我没看到你用。。。。
页: [1]
查看完整版本: kmalloc申请的内存映射到用户态的问题