内核 kmap_atomic分析
2016-03-09 02:29
246 查看
1、为什么会有这个函数?
我想主要的原因就是kmap_atomic在效率上要比kmap提升不少。
在kmap函数中,会有如下几个比较耗时的部分:
1)、page_address函数
2)、Sleep for somebody else to unmap their entries
代码如下:
171 ____________DECLARE_WAITQUEUE(wait, current);
173 ______________set_current_state(TASK_UNINTERRUPTIBLE);
174____________add_wait_queue(&pkmap_map_wait,&wait);
175 ____________unlock_kmap();
176 ____________schedule();
177 ____________remove_wait_queue(&pkmap_map_wait, &wait);
3)、lock_kmap函数,需要加锁啊!!!
相反kmap_atomic函数,从名字就能看出,原子操作一气呵成。。没有sleep,没有锁。
2、怎么实现的?需要考虑什么问题?
首先得知道这个函数的主要目的是实现page 到 vaddr的转化。
其次我们得考虑多cpu和多任务,大家知道 kernel可以在多个cpu上同时运行不同的task,然而它们共同使用一个内存地址空间,因此如何能保证N个cpu调用kmap_atomic不会将page映射到一个虚拟地址(vaddr)呢?
我们来看看kmap_atomic是如何实现的?
1)、定义一个percpu变量__kmap_atomic_idx,同时在当前cpu上禁用抢占,直到unmap的时候才开启,这样就保证了同一cpu其它任务不会调用该函数。除非该进程在unmap之前睡眠,如果真的那样,别的进程就很可能在同一cpu重入kmap_atomic函数了,然后就可能映射到同一虚拟地址,因此在原子映射期间最好不要休眠。
2)、设计了一个完美的公式
type = kmap_atomic_idx_push(); //递增一个percpu变量,返回递增后的结果,增加一个计算type的函数表示kmap_atomic函数可以重入,就是上面说的该进程在unmap之前睡眠情况,但一般情况下不会发生冲入,所以该值应该是1,unmap时该值会--。当然重入的次数是有限制的,不会超过KM_TYPE_NR。
idx = type + KM_TYPE_NR*smp_processor_id();//不同的cpusmp_processor_id()的值是不同的,因此,不同的cpu得到的idx不同。
vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);//不同的idx得到的vaddr就不同了。
set_pte(kmap_pte-idx, mk_pte(page, prot)); //设置页表
return (void *)vaddr;
总结,通过percpu变量+禁抢占+加计算公式,就实现了,不同cpu的不同进程调用kmap_atomic函数得到的vaddr是不同的。同时这也给我们实现atomic提供了一种思路,实际上atomic一般都会有一个percpu变量。
3、得到vaddr之后,页表的建立。
我们就以arm为例,来说明一下,arm初始化的时候,会通过create_mapping来创建一些页表,比如map_lowmem就会调用create_mapping函数来建立整个低端内存的映射。
同样地在函数devicemaps_init中会有如下代码:
967 ____map.pfn = __phys_to_pfn(virt_to_phys(vectors_page));
968 ____map.virtual = 0xffff0000;
969 ____map.length = PAGE_SIZE;
970 ____map.type = MT_HIGH_VECTORS;
971 ____create_mapping(&map);
这里有一点需要说明一点,虽然只映射了一个PAGE_SIZE,但是如果内核采用的二级页表(即存在pgd,pte),而不是一级页表(只有pgd)的话,会在create_mapping函数中,分配512个pte项(即512个pte指针ptep),远远大于KM_TYPE_NR个数。但是如果采用一级页表的话,这就会有问题,这种情况应该不能用kmap_atomic函数,因为通过TOP_PTE(vaddr)宏是得不到ptep的,因此目前代码中有如下保护:
67 #ifdef CONFIG_DEBUG_HIGHMEM
68 ____/*
69 ____ * With debugging enabled, kunmap_atomic forces that entry to 0.
70 ____ * Make sure it was indeed properly unmapped.
71 ____ */
72____BUG_ON(!pte_none((TOP_PTE(vaddr))));
73 #endif
由此,我们可以看到kmap_atomic使用的是地址空间顶部的一小段地址空间(0xffff0000开始),内核逻辑将这一小段地址空间分成了若干个节(slot),每一节的大小是一个page的大小,可以用来映射一个page。虽然总共可用512个slot,但是只能用KM_TYPE_NR个,有点遗憾!!!
原文地址: http://blog.chinaunix.net/uid-26817832-id-3358944.html
我想主要的原因就是kmap_atomic在效率上要比kmap提升不少。
在kmap函数中,会有如下几个比较耗时的部分:
1)、page_address函数
2)、Sleep for somebody else to unmap their entries
代码如下:
171 ____________DECLARE_WAITQUEUE(wait, current);
173 ______________set_current_state(TASK_UNINTERRUPTIBLE);
174____________add_wait_queue(&pkmap_map_wait,&wait);
175 ____________unlock_kmap();
176 ____________schedule();
177 ____________remove_wait_queue(&pkmap_map_wait, &wait);
3)、lock_kmap函数,需要加锁啊!!!
相反kmap_atomic函数,从名字就能看出,原子操作一气呵成。。没有sleep,没有锁。
2、怎么实现的?需要考虑什么问题?
首先得知道这个函数的主要目的是实现page 到 vaddr的转化。
其次我们得考虑多cpu和多任务,大家知道 kernel可以在多个cpu上同时运行不同的task,然而它们共同使用一个内存地址空间,因此如何能保证N个cpu调用kmap_atomic不会将page映射到一个虚拟地址(vaddr)呢?
我们来看看kmap_atomic是如何实现的?
1)、定义一个percpu变量__kmap_atomic_idx,同时在当前cpu上禁用抢占,直到unmap的时候才开启,这样就保证了同一cpu其它任务不会调用该函数。除非该进程在unmap之前睡眠,如果真的那样,别的进程就很可能在同一cpu重入kmap_atomic函数了,然后就可能映射到同一虚拟地址,因此在原子映射期间最好不要休眠。
2)、设计了一个完美的公式
type = kmap_atomic_idx_push(); //递增一个percpu变量,返回递增后的结果,增加一个计算type的函数表示kmap_atomic函数可以重入,就是上面说的该进程在unmap之前睡眠情况,但一般情况下不会发生冲入,所以该值应该是1,unmap时该值会--。当然重入的次数是有限制的,不会超过KM_TYPE_NR。
idx = type + KM_TYPE_NR*smp_processor_id();//不同的cpusmp_processor_id()的值是不同的,因此,不同的cpu得到的idx不同。
vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);//不同的idx得到的vaddr就不同了。
set_pte(kmap_pte-idx, mk_pte(page, prot)); //设置页表
return (void *)vaddr;
总结,通过percpu变量+禁抢占+加计算公式,就实现了,不同cpu的不同进程调用kmap_atomic函数得到的vaddr是不同的。同时这也给我们实现atomic提供了一种思路,实际上atomic一般都会有一个percpu变量。
3、得到vaddr之后,页表的建立。
我们就以arm为例,来说明一下,arm初始化的时候,会通过create_mapping来创建一些页表,比如map_lowmem就会调用create_mapping函数来建立整个低端内存的映射。
同样地在函数devicemaps_init中会有如下代码:
967 ____map.pfn = __phys_to_pfn(virt_to_phys(vectors_page));
968 ____map.virtual = 0xffff0000;
969 ____map.length = PAGE_SIZE;
970 ____map.type = MT_HIGH_VECTORS;
971 ____create_mapping(&map);
这里有一点需要说明一点,虽然只映射了一个PAGE_SIZE,但是如果内核采用的二级页表(即存在pgd,pte),而不是一级页表(只有pgd)的话,会在create_mapping函数中,分配512个pte项(即512个pte指针ptep),远远大于KM_TYPE_NR个数。但是如果采用一级页表的话,这就会有问题,这种情况应该不能用kmap_atomic函数,因为通过TOP_PTE(vaddr)宏是得不到ptep的,因此目前代码中有如下保护:
67 #ifdef CONFIG_DEBUG_HIGHMEM
68 ____/*
69 ____ * With debugging enabled, kunmap_atomic forces that entry to 0.
70 ____ * Make sure it was indeed properly unmapped.
71 ____ */
72____BUG_ON(!pte_none((TOP_PTE(vaddr))));
73 #endif
由此,我们可以看到kmap_atomic使用的是地址空间顶部的一小段地址空间(0xffff0000开始),内核逻辑将这一小段地址空间分成了若干个节(slot),每一节的大小是一个page的大小,可以用来映射一个page。虽然总共可用512个slot,但是只能用KM_TYPE_NR个,有点遗憾!!!
原文地址: http://blog.chinaunix.net/uid-26817832-id-3358944.html
相关文章推荐
- 夺命雷公狗---微信开发52----网页授权(oauth2.0)获取用户基本信息接口(2)
- php 防sql注入?
- 自己动手写一个简单的MVC框架(第二版)
- ToolBar学习
- 2_JavaScript日期格式化
- Pycharm5注册方式
- android ndk获取外置SD沙盒目录的方法
- 学习是为了创造更美好的未来
- 随机种子
- 关于两条链表交叉(交叉有环)的问题解决
- 正则表达式
- iterate the vertices around a vertex
- 100 个极其有用的搜索引擎
- swift,NSUserDefaults的swift化封装
- iOS:webView中图片自适应屏幕的一种解决方案
- LeetCode 206. Reverse Linked List
- Move Zeroes
- com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
- 膝盖中了一箭之康复篇-第八个月暨2月份目标总结
- 阿里云 api 的文档拼写错误