内存映射函数remap_pfn_range学习——示例分析(1)
span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }.cm-searching {background: #ffa; background: rgba(255, 255, 0, .4);}.cm-force-border { padding-right: .1px; }@media print { .CodeMirror div.CodeMirror-cursors {visibility: hidden;}}.cm-tab-wrap-hack:after { content: ""; }span.CodeMirror-selectedtext { background: none; }.CodeMirror-activeline-background, .CodeMirror-selected {transition: visibility 0ms 100ms;}.CodeMirror-blur .CodeMirror-activeline-background, .CodeMirror-blur .CodeMirror-selected {visibility:hidden;}.CodeMirror-blur .CodeMirror-matchingbracket {color:inherit !important;outline:none !important;text-decoration:none !important;}
-->
li {list-style-type:decimal;}ol.wiz-list-level2 > li {list-style-type:lower-latin;}ol.wiz-list-level3 > li {list-style-type:lower-roman;}blockquote {padding:0 12px;padding:0 0.75rem;}blockquote > :first-child {margin-top:0;}blockquote > :last-child {margin-bottom:0;}img {border:0;max-width:100%;height:auto !important;margin:2px 0;}table {border-collapse:collapse;border:1px solid #bbbbbb;}td, th {padding:4px 8px;border-collapse:collapse;border:1px solid #bbbbbb;min-height:28px;word-break:break-all;box-sizing: border-box;}.wiz-hide {display:none !important;}
-->
作者
平台
参考
概述
/**
* remap_pfn_range - remap kernel memory to userspace
* @vma: user vma to map to
* @addr: target user address to start at
* @pfn: physical address of kernel memory
* @size: size of map area
* @prot: page protection flags for this mapping
*
* Note: this is only safe if the mm semaphore is held when called.
*/
int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,
unsigned long pfn, unsigned long size, pgprot_t prot);
正文
一、驱动程序
static int __init remap_pfn_init(void)
{
int ret = ; kbuff = kzalloc(BUF_SIZE, GFP_KERNEL); // 这里的BUF_SIZE是128KB
if (!kbuff) {
ret = -ENOMEM;
goto err;
} ret = misc_register(&remap_pfn_misc); // 注册一个misc设备
if (unlikely(ret)) {
pr_err("failed to register misc device!\n");
goto err;
} return ; err:
return ret;
}
第11行注册了一个misc设备,相关信息如下:
static struct miscdevice remap_pfn_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "remap_pfn",
.fops = &remap_pfn_fops,
};
这样加载驱动后会在/dev下生成一个名为remap_pfn的节点,用户程序可以通过这个节点跟驱动通信。其中remap_pfn_fops的定义如下:
static const struct file_operations remap_pfn_fops = {
.owner = THIS_MODULE,
.open = remap_pfn_open,
.mmap = remap_pfn_mmap,
};
第3行的open函数这里没有做什么实际的工作,只是打印一些log,比如将进程的内存布局信息输出
static int remap_pfn_open(struct inode *inode, struct file *file)
{
struct mm_struct *mm = current->mm; printk("client: %s (%d)\n", current->comm, current->pid);
printk("code section: [0x%lx 0x%lx]\n", mm->start_code, mm->end_code);
printk("data section: [0x%lx 0x%lx]\n", mm->start_data, mm->end_data);
printk("brk section: s: 0x%lx, c: 0x%lx\n", mm->start_brk, mm->brk);
printk("mmap section: s: 0x%lx\n", mm->mmap_base);
printk("stack section: s: 0x%lx\n", mm->start_stack);
printk("arg section: [0x%lx 0x%lx]\n", mm->arg_start, mm->arg_end);
printk("env section: [0x%lx 0x%lx]\n", mm->env_start, mm->env_end); return ;
}
static int remap_pfn_mmap(struct file *file, struct vm_area_struct *vma)
{
unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
unsigned long pfn_start = (virt_to_phys(kbuff) >> PAGE_SHIFT) + vma->vm_pgoff;
unsigned long virt_start = (unsigned long)kbuff + offset;
unsigned long size = vma->vm_end - vma->vm_start;
int ret = ; printk("phy: 0x%lx, offset: 0x%lx, size: 0x%lx\n", pfn_start << PAGE_SHIFT, offset, size); ret = remap_pfn_range(vma, vma->vm_start, pfn_start, size, vma->vm_page_prot);
if (ret)
printk("%s: remap_pfn_range failed at [0x%lx 0x%lx]\n",
__func__, vma->vm_start, vma->vm_end);
else
printk("%s: map 0x%lx to 0x%lx, size: 0x%lx\n", __func__, virt_start,
vma->vm_start, size); return ret;
}
第3行的vma_pgoff表示的是该vma表示的区间在缓冲区中的偏移地址,单位是页。这个值是用户调用mmap时传入的最后一个参数,不过用户空间的offset的单位是字节(当然必须是页对齐),进入内核后,内核会将该值右移PAGE_SHIFT(12),也就是转换为以页为单位。因为要在第9行打印这个编译地址,所以这里将其再左移PAGE_SHIFT,然后赋值给offset。
二、用户测试程序
#define PAGE_SIZE (4*1024)
#define BUF_SIZE (16*PAGE_SIZE)
#define OFFSET (0) int main(int argc, const char *argv[])
{
int fd;
char *addr = NULL; fd = open("/dev/remap_pfn", O_RDWR); addr = mmap(NULL, BUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, fd, OFFSET); sprintf(addr, "I am %s\n", argv[]); while()
sleep();
return ;
}
第10和第12行,打开设备节点,然后从内核空间映射64KB的内存到用户空间,首地址存放在addr中,由于后面既要写入也要共享,所以设置了对应的flags。这里指定的offset是0,即映射前64KB。
#define PAGE_SIZE (4*1024)
#define BUF_SIZE (16*PAGE_SIZE)
#define OFFSET (0) int main(int argc, const char *argv[])
{
int fd;
char *addr = NULL; fd = open("/dev/remap_pfn", O_RDWR); addr = mmap(NULL, BUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, fd, OFFSET); printf("%s", addr); while()
sleep(); return ;
}
user_2跟user_1实现一般一样,不同之处是将addr指向的虚拟地址空间的内容打印出来。
#define PAGE_SIZE (4*1024)
#define BUF_SIZE (16*PAGE_SIZE)
#define OFFSET (16*PAGE_SIZE) int main(int argc, const char *argv[])
{
int fd;
char *addr = NULL; fd = open("/dev/remap_pfn", O_RDWR); addr = mmap(NULL, BUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, fd, OFFSET); sprintf(addr, "I am %s\n", argv[]); while()
sleep();
return ;
}
第12行的OFFSET设置的是64KB,表示将内核缓冲区的后64KB映射到用户空间
#define PAGE_SIZE (4*1024)
#define BUF_SIZE (16*PAGE_SIZE)
#define OFFSET (16*PAGE_SIZE) int main(int argc, const char *argv[])
{
int fd;
char *addr = NULL; fd = open("/dev/remap_pfn", O_RDWR); addr = mmap(NULL, BUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, fd, OFFSET); printf("%s", addr); while()
sleep();
return ;
}
#define PAGE_SIZE (4*1024)
#define BUF_SIZE (32*PAGE_SIZE)
#define OFFSET (0) int main(int argc, const char *argv[])
{
int fd;
char *addr = NULL;
int *brk; fd = open("/dev/remap_pfn", O_RDWR); addr = mmap(NULL, BUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, fd, );
memset(addr, 0x0, BUF_SIZE); printf("Clear Finished\n"); while()
sleep();
return ;
}
三、测试
1、内核空间的虚拟内存布局
[ 0.000000] Virtual kernel memory layout:
[ 0.000000] vector : 0xffff0000 - 0xffff1000 ( kB)
[ 0.000000] fixmap : 0xffc00000 - 0xfff00000 ( kB)
[ 0.000000] vmalloc : 0xf0800000 - 0xff800000 ( MB)
[ 0.000000] lowmem : 0xc0000000 - 0xf0000000 ( MB)
[ 0.000000] pkmap : 0xbfe00000 - 0xc0000000 ( MB)
[ 0.000000] modules : 0xbf000000 - 0xbfe00000 ( MB)
[ 0.000000] .text : 0xc0008000 - 0xc0800000 ( kB)
[ 0.000000] .init : 0xc0b00000 - 0xc0c00000 ( kB)
[ 0.000000] .data : 0xc0c00000 - 0xc0c7696c ( kB)
[ 0.000000] .bss : 0xc0c78000 - 0xc0cc9b8c ( kB)
2、用户虚拟地址空间的布局
3、user_1和user_2
[root@vexpress mnt]# ./user_1
可以看到如下内核log:
[ 2494.835749] client: user_1 ()
[ 2494.835918] code section: [0x8000 0x87f4]
[ 2494.836047] data section: [0x107f4 0x1092c]
[ 2494.836165] brk section: s: 0x11000, c: 0x11000
[ 2494.836307] mmap section: s: 0xb6f17000
[ 2494.836441] stack section: s: 0xbe909e20
[ 2494.836569] arg section: [0xbe909f23 0xbe909f2c]
[ 2494.836689] env section: [0xbe909f2c 0xbe909ff3]
[ 2494.836943] phy: 0x8eb60000, offset: 0x0, size: 0x10000
[ 2494.837176] remap_pfn_mmap: map 0xeeb60000 to 0xb6d75000, size: 0x10000
进程号是870,可以分别用下面的查看一下该进程的地址空间的map信息:
[root@vexpress mnt]# cat /proc//maps
- r-xp : /mnt/user_1
- rw-p : /mnt/user_1
b6d75000-b6d85000 rw-s : /dev/remap_pfn
b6d85000-b6eb8000 r-xp b3: /lib/libc-2.18.so
b6eb8000-b6ebf000 ---p b3: /lib/libc-2.18.so
b6ebf000-b6ec1000 r--p b3: /lib/libc-2.18.so
b6ec1000-b6ec2000 rw-p b3: /lib/libc-2.18.so
b6ec2000-b6ec5000 rw-p :
b6ec5000-b6ee6000 r-xp b3: /lib/libgcc_s.so.
b6ee6000-b6eed000 ---p b3: /lib/libgcc_s.so.
b6eed000-b6eee000 rw-p b3: /lib/libgcc_s.so.
b6eee000-b6f0e000 r-xp b3: /lib/ld-2.18.so
b6f13000-b6f15000 rw-p :
b6f15000-b6f16000 r--p 0001f000 b3: /lib/ld-2.18.so
b6f16000-b6f17000 rw-p b3: /lib/ld-2.18.so
be8e9000-be90a000 rw-p : [stack]
bed1c000-bed1d000 r-xp : [sigpage]
bed1d000-bed1e000 r--p : [vvar]
bed1e000-bed1f000 r-xp : [vdso]
ffff0000-ffff1000 r-xp : [vectors]
上面的每一行都可以表示一个vma的映射信息,其中第4行是需要关心的:
b6d75000-b6d85000 rw-s : /dev/remap_pfn
含义:
[root@vexpress mnt]# pmap -x
: {no such process} ./user_1
Address Kbytes PSS Dirty Swap Mode Mapping
r-xp /mnt/user_1
rw-p /mnt/user_1
b6d75000 rw-s /dev/remap_pfn
b6d85000 r-xp /lib/libc-2.18.so
b6eb8000 ---p /lib/libc-2.18.so
b6ebf000 r--p /lib/libc-2.18.so
b6ec1000 rw-p /lib/libc-2.18.so
b6ec2000 rw-p [ anon ]
b6ec5000 r-xp /lib/libgcc_s.so.
b6ee6000 ---p /lib/libgcc_s.so.
b6eed000 rw-p /lib/libgcc_s.so.
b6eee000 r-xp /lib/ld-2.18.so
b6f13000 rw-p [ anon ]
b6f15000 r--p /lib/ld-2.18.so
b6f16000 rw-p /lib/ld-2.18.so
be8e9000 rw-p [stack]
bed1c000 r-xp [sigpage]
bed1d000 r--p [vvar]
bed1e000 r-xp [vdso]
ffff0000 r-xp [vectors]
-------- ------ ------ ------ ------
total
[root@vexpress mnt]# ./user_2
I am ./user_1
可以看到user_1写入的信息,下面是内核log以及虚拟地址空间映射信息:
[ 2545.832903] client: user_2 ()
[ 2545.833087] code section: [0x8000 0x87e0]
[ 2545.833178] data section: [0x107e0 0x10918]
[ 2545.833262] brk section: s: 0x11000, c: 0x11000
[ 2545.833346] mmap section: s: 0xb6fb5000
[ 2545.833423] stack section: s: 0xbea0ee20
[ 2545.833499] arg section: [0xbea0ef23 0xbea0ef2c]
[ 2545.833590] env section: [0xbea0ef2c 0xbea0eff3]
[ 2545.833761] phy: 0x8eb60000, offset: 0x0, size: 0x10000
[ 2545.833900] remap_pfn_mmap: map 0xeeb60000 to 0xb6e13000, size: 0x10000 [root@vexpress mnt]# cat /proc//maps
- r-xp : /mnt/user_2
- rw-p : /mnt/user_2
b6e13000-b6e23000 rw-s : /dev/remap_pfn
b6e23000-b6f56000 r-xp b3: /lib/libc-2.18.so
b6f56000-b6f5d000 ---p b3: /lib/libc-2.18.so
b6f5d000-b6f5f000 r--p b3: /lib/libc-2.18.so
b6f5f000-b6f60000 rw-p b3: /lib/libc-2.18.so
b6f60000-b6f63000 rw-p :
b6f63000-b6f84000 r-xp b3: /lib/libgcc_s.so.
b6f84000-b6f8b000 ---p b3: /lib/libgcc_s.so.
b6f8b000-b6f8c000 rw-p b3: /lib/libgcc_s.so.
b6f8c000-b6fac000 r-xp b3: /lib/ld-2.18.so
b6fb0000-b6fb3000 rw-p :
b6fb3000-b6fb4000 r--p 0001f000 b3: /lib/ld-2.18.so
b6fb4000-b6fb5000 rw-p b3: /lib/ld-2.18.so
be9ee000-bea0f000 rw-p : [stack]
beedf000-beee0000 r-xp : [sigpage]
beee0000-beee1000 r--p : [vvar]
beee1000-beee2000 r-xp : [vdso]
ffff0000-ffff1000 r-xp : [vectors]
上面的的log信息可以查看: https://github.com/pengdonglin137/remap_pfn_demo/blob/master/log/user2
4、user_3和user_4
[ 4938.000918] client: user_3 ()
[ 4938.001117] code section: [0x8000 0x87f4]
[ 4938.001205] data section: [0x107f4 0x1092c]
[ 4938.001281] brk section: s: 0x11000, c: 0x11000
[ 4938.001410] mmap section: s: 0xb6ff1000
[ 4938.001485] stack section: s: 0xbea10e20
[ 4938.001549] arg section: [0xbea10f23 0xbea10f2c]
[ 4938.001606] env section: [0xbea10f2c 0xbea10ff3]
[ 4938.001793] phy: 0x8eb70000, offset: 0x10000, size: 0x10000
[ 4938.001996] remap_pfn_mmap: map 0xeeb70000 to 0xb6e4f000, size: 0x10000 [root@vexpress mnt]#
[root@vexpress mnt]# cat /proc//maps
- r-xp : /mnt/user_3
- rw-p : /mnt/user_3
b6e4f000-b6e5f000 rw-s : /dev/remap_pfn
b6e5f000-b6f92000 r-xp b3: /lib/libc-2.18.so
b6f92000-b6f99000 ---p b3: /lib/libc-2.18.so
b6f99000-b6f9b000 r--p b3: /lib/libc-2.18.so
b6f9b000-b6f9c000 rw-p b3: /lib/libc-2.18.so
b6f9c000-b6f9f000 rw-p :
b6f9f000-b6fc0000 r-xp b3: /lib/libgcc_s.so.
b6fc0000-b6fc7000 ---p b3: /lib/libgcc_s.so.
b6fc7000-b6fc8000 rw-p b3: /lib/libgcc_s.so.
b6fc8000-b6fe8000 r-xp b3: /lib/ld-2.18.so
b6fed000-b6fef000 rw-p :
b6fef000-b6ff0000 r--p 0001f000 b3: /lib/ld-2.18.so
b6ff0000-b6ff1000 rw-p b3: /lib/ld-2.18.so
be9f0000-bea11000 rw-p : [stack]
bebe9000-bebea000 r-xp : [sigpage]
bebea000-bebeb000 r--p : [vvar]
bebeb000-bebec000 r-xp : [vdso]
ffff0000-ffff1000 r-xp : [vectors]
需要关注的是第16行,其中的"00010000"表示offset,大小是64KB,也就是vma->vm_pgoff的值。
5、user_5
内存映射函数remap_pfn_range学习——示例分析(1)的更多相关文章
- 内存映射函数remap_pfn_range学习——示例分析(2)
li {list-style-type:decimal;}ol.wiz-list-level2 > li {list-style-type:lower-latin;}ol.wiz-list-le ...
- 内存映射函数remap_pfn_range学习——代码分析(3)
li {list-style-type:decimal;}ol.wiz-list-level2 > li {list-style-type:lower-latin;}ol.wiz-list-le ...
- ROS_Kinetic_29 kamtoa simulation学习与示例分析(一)
致谢源代码网址:https://github.com/Tutorgaming/kamtoa-simulation kamtoa simulation学习与示例分析(一) 源码学习与分析是学习ROS,包 ...
- 大数据下基于Tensorflow框架的深度学习示例教程
近几年,信息时代的快速发展产生了海量数据,诞生了无数前沿的大数据技术与应用.在当今大数据时代的产业界,商业决策日益基于数据的分析作出.当数据膨胀到一定规模时,基于机器学习对海量复杂数据的分析更能产生较 ...
- JVM内存状况查看方法和分析工具
Java本身提供了多种丰富的方法和工具来帮助开发人员查看和分析GC及JVM内存的状况,同时开源界和商业界也有一些工具可用于查看.分析GC及JVM内存的状况.通过这些分析,可以排查程序中内存泄露的问题及 ...
- zigbee学习:示例程序SampleApp中通讯流程
zigbee学习:示例程序SampleApp中通讯流程 本文博客链接:http://blog.csdn.net/jdh99,作者:jdh,转载请注明. 参考链接: http://wjf88223.bl ...
- 【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
[嵌入式开发]ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 ) 一. 内存 ...
- osg学习示例之遇到问题四骨骼动画编译osgCal
osg学习示例之遇到问题四骨骼动画编译osgCal 转自:http://blog.csdn.net/wuwangrun/article/details/8239451 今天学到书<OpenSce ...
- 大并发连接的oracle在Linux下内存不足的问题的分析
大并发连接的oracle在Linux下内存不足的问题的分析 2010-01-28 20:06:21 分类: Oracle 最近一台装有Rhel5.3的40G内存的机器上有一个oracle数据库,数据库 ...
随机推荐
- 『实践』Yalmip+Ipopt+Cplex使用手册
Yalmip+Ipopt+Cplex使用手册 1.软件版本 Cplex 12.6.2,Matlab R2014a,Ipopt 3.12.9,Yalmip 2.Cplex添加方法 官方下载地址: htt ...
- python里面的引用
1 对象及其引用 python中,引用是用命名空间来实现的,命名空间维护了变量和对象之间的引用关系. myInt = 27 yourInt = myInt #change the value of y ...
- 最完整的PS快捷键大全(绝对经典)
快速恢复默认值 有些不擅长Photoshop的朋友为了调整出满意的效果真是几经周折,结果发现还是原来的默认效果最好,这下傻了眼,后悔不该当初呀!怎么恢复到默认值呀?试着轻轻点按选项栏上的工具图标,然后 ...
- python tar.gz格式压缩、解压
一.压缩 需求描述 现在有一个目录,需要将此目录打包成tar.gz文件.因为有一个Django项目,需要用到此功能! tar.gz 目录结构如下: ./ ├── folder │ ├── .doc ...
- js replace,正则截取字符串内容
1.js replace替换,使用 http://www.jb51.net/article/43949.htm 顺便记录一下 e.g. js获取sql中的可替换参数$id,$name."SE ...
- 关于.NetCore 2.0 迁移到2.1的一些建议和问题
最近手欠升级了下VS2017到15.7版本 然后更新了下sdk到2.1.300版本 那么麻烦就来了,原有项目就带来了很多问题,更新所有包到最新就不用说了 下面说明下最主要存在的2问题: 第一个问题:调 ...
- 一步一步学习IdentityServer3 (4)
其实上述例子 很多都很找到 但是在实际生态环境中给例子有很多不一样的地方 比如自定已登录界面怎么做? 怎么访问自己的用户数据库实现登录? 怎么在接口中使用,在接口中又怎么实现与Idr3结合授权? 等等 ...
- 006 jquery过滤选择器-----------(可见性过滤选择器)
1.介绍 2.程序 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> < ...
- SOCKET简单爬虫实现代码和使用方法
抓取一个网页内容非常容易,常见的方式有curl.file_get_contents.socket以及文件操作函数file.fopen等. 下面使用SOCKET下的fsockopen()函数访问Web服 ...
- 接口调试工具ApiPost的发送超时时间设置方法
有部分使用ApiPost的同学反应:发送接口调试时,响应超时时间设置的太短导致接口访问失败,怎么设置呢? 就连百度也有很多人在搜: 今天就来说一说. ApiPost简介: ApiPost是一个支持团队 ...