免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3165 | 回复: 5
打印 上一主题 下一主题

谁有mmap与用户层通信的代码? [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-04-30 16:39 |只看该作者 |倒序浏览
找了一个简单的,但有问题。
内核模块:
static unsigned long p = 0;
static int __init init(void)
{
     p = __get_free_pages(GFP_KERNEL, 0); //
     SetPageReserved(virt_to_page(p));
     printk("<1> p = 0x%08x\n", p);
     strcpy(p, "Hello world!\n");
     return 0;
}

static void __exit fini(void)
{
        ClearPageReserved(virt_to_page(p));
     free_pages(p, 0);

        return;
}
module_init(init);
module_exit(fini);

用户层程序:
#define PAGE_SIZE (4*1024)
#define PAGE_OFFSET         0xc0000000
#define KERNEL_VIRT_ADDR     0xcf9e5000 //这里是硬编址的, 可以通过ioctl 或者proc来实现的。


int main()
{
     char *buf;
     int fd;
     unsigned long phy_addr;

     fd=open("/dev/mem",O_RDWR);
     if(fd == -1)
         perror("open");
     phy_addr=KERNEL_VIRT_ADDR - PAGE_OFFSET;

     buf=mmap(0, PAGE_SIZE,
         PROT_READ|PROT_WRITE, MAP_SHARED,
         fd, phy_addr);
     if(buf == MAP_FAILED)
         perror("mmap");
     puts(buf);//打印共享内存的内容

     munmap(buf,PAGE_SIZE);

     close(fd);
     return 0;
}

模块可以正常加载,但用户程序运行会崩溃,就是物理地址(phy_addr)那里出错,应该怎样确定该地址?

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
2 [报告]
发表于 2010-04-30 16:47 |只看该作者
我这里有一篇总结,还有源码,LZ看一下:
http://blog.chinaunix.net/u/33048/showart_1271210.html

论坛徽章:
0
3 [报告]
发表于 2010-04-30 23:19 |只看该作者
GOD老兄,你那篇BLOG里只有提到PROC,没有说MMAP啊。

回LZ:
#define KERNEL_VIRT_ADDR     0xcf9e5000
这个地址应该是你的KERNEL MODULE 的PRINTK那行打印出来的地址,不同机器上不一样的。你改了没?

论坛徽章:
0
4 [报告]
发表于 2010-05-01 00:54 |只看该作者
模块内没有实现mmap。

示范代码
  1. /*内核模块加载函数*/
  2. int __init kmalloc_map_init(void)
  3. {
  4.     //申请设备号,添加cdev结构体
  5.     buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
  6.     for (page = virt_to_page(buffer); page < virt_to_page(buffer + BUFFER_SIZE); page++)
  7.     {
  8.         mem_map_reserve(page);//置页为保留,virt_to_page()将内核虚拟地址转化为页
  9.     }
  10. }
  11. /*mmap()函数*/
  12. static int kmalloc_map_mmap(struct file *filp, struct vm_area_struct *vma)
  13. {
  14.     unsigned long page, pos;
  15.     unsigned long start = (unsigned long)vma->vm_start;
  16.     unsigned long size = (unsigned long)(vma->vm_end - vma->vm_start);
  17.     if (size > BUFFER_SIZE)
  18.     {
  19.         return - EINVAL;
  20.     }
  21.     pos = (unsigned long)buffer;
  22.     /*映射buffer中的所有页*/
  23.     while (size > 0)
  24.     {
  25.         page = virt_to_phys((void *)pos);
  26.         if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED))
  27.             return - EAGAIN;
  28.         start += PAGE_SIZE;
  29.         pos += PAGE_SIZE;
  30.         size -= PAGE_SIZE;
  31.     }

  32. /*

  33. **可否用io_remap_pfn_range(vma, vma->vm_start, virt_to_phys((void *)buffer) >> PAGE_SHIFT, vma->vm_end - vma->vm_start, PAGE_SHARED)来替代remap_page_range?

  34. **在Linux kernel 2.6.27中,已经找不到remap_page_range的实现,见Linux kernel change log: Changes remap_page_range to remap_pfn_range for 2.6.10 and above kernels

  35. */
  36.     return 0;
  37. }

复制代码
【Note】:mem_map_reserve是2.4版本内核的函数,2.6内核用SetPageReserved取代之

论坛徽章:
0
5 [报告]
发表于 2010-05-01 23:19 |只看该作者
前几天 在学习linux内核的内存管理时候找到了这么一个例子,不知道是否符合楼主的要求:
2.6.tar.gz (3.31 KB, 下载次数: 101)

下面是这个例子的说明:
实例希望利用内存映射,将系统内核中的一部分虚拟内存映射到用户空间,以供应用程序读取——你可利用它进行内核空间到用户空间的大规模信息传输。因此我们将试图写一个虚拟字符设备驱动程序,通过它将系统内核空间映射到用户空间——将内核虚拟内存映射到用户虚拟地址。我们的例子程序将演示把vmalloc分配的内核虚拟地址映射到用户地址空间的全过程。

程序里主要应解决两个问题:

第一是将vmalloc分配的内核虚拟内存正确地转化成物理地址

因为内存映射先要获得被映射的物理地址,然后才能将其映射到要求的用户虚拟地址上。我们已经看到内核物理内存映射区域中的地址可以被内核函数virt_to_phys转换成实际的物理内存地址,但对于vmalloc分配的内核虚拟地址无法直接转化成物理地址,所以我们必须对这部分虚拟内存格外“照顾”——先将其转化成内核物理内存映射区域中的地址,然后在用virt_to_phys变为物理地址。

转化工作需要进行如下步骤:

a)         找到vmalloc虚拟内存对应的页表,并寻找到对应的页表项。

b)        获取页表项对应的页面指针

c)        通过页面得到对应的内核物理内存映射区域地址。
第二是当访问vmalloc分配区时,如果发现虚拟内存尚未被映射到物理页,则需要处理“缺页异常”。因此需要我们实现内存区域中的nopaga操作,以能返回被映射的物理页面指针,在我们的实例中就是返回上面过程中的内核物理内存映射区域中的地址。由于vmalloc分配的虚拟地址与物理地址的对应关系并非分配时就可确定,必须在缺页现场建立页表,因此这里不能使用remap_page_range方法,只能用vma的nopage方法一页一页的建立

程序组成
map_driver.c,它是以模块形式加载的虚拟字符驱动程序。该驱动负责将一定长的内核虚拟地址(vmalloc分配的)映射到设备文件上。其中主要的函数有——vaddress_to_kaddress()负责对vmalloc分配的地址进行页表解析,以找到对应的内核物理映射地址(kmalloc分配的地址);map_nopage()负责在进程访问一个当前并不存在的VMA页时,寻找该地址对应的物理页,并返回该页的指针。

test.c 它利用上述驱动模块对应的设备文件在用户空间读取读取内核内存。结果可以看到内核虚拟地址的内容(ok!),被显示在了屏幕上

PS:我也是内核新手,对这个例子是看到云里雾里的。如果这个例子真的对楼主有帮助,而楼主又能将其轻易看懂的话,希望能给我这个菜鸟讲解一下

论坛徽章:
0
6 [报告]
发表于 2010-07-30 17:01 |只看该作者
前几天 在学习linux内核的内存管理时候找到了这么一个例子,不知道是否符合楼主的要求:


下面是这个例 ...
cu_liang 发表于 2010-05-01 23:19



    它这段代码应该是有问题的,不知道LS发现了没?
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP