kmap_atomic的细节以及改进

简介:
kmap_atomic用于高端内存映射,用于紧急的,短时间的映射,它没有使用任何锁,完全靠一个数学公式来避免混乱,它空间有限且虚拟地址固定,这意味着它映射的内存不能长期被占用而不被unmap,kmap_atomic在效率上要比kmap提升不少,然而它和kmap却不是用于同一场合的。不管怎么说,它的设计是很完美的。
     kernel可以在多个cpu上同时运行不同的task,然而它们共同使用一个内存地址空间,也就是说,地址空间对于多个cpu看到的是同一个,kmap_atomic使用的是地址空间顶部的一小段地址空间,内核逻辑将这一小段地址空间分成了若干个节,每一节的大小是一个page的大小,可以用来映射一个page,根据公用地址空间的原理,所有的cpu共同使用这些节,因此如何能保证N个cpu调用kmap_atomic不会将page映射到一个地址呢?这就是这个数学公式所起的作用:
idx = type + KM_TYPE_NR*smp_processor_id();
vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
其中KM_TYPE_NR代表type的最大值加1:
enum km_type {
    KM_BOUNCE_READ,
    KM_SKB_SUNRPC_DATA,
    KM_SKB_DATA_SOFTIRQ,
    KM_USER0,
    KM_USER1,
...
    KM_TYPE_NR
};
调用kmap_atomic的时候,有一个参数就是上述的枚举类型km_type,这样不同的cpu得到的vaddr就不可能一样了,原因是这样的:type + KM_TYPE_NR*smp_processor_id()可以写成z=x+N*y(x<N),只要y不同,z就一定不会相同,因为z=x+N*y<N+N*y=(N+1)*y,设现有y1<y2(二者最少相差1),则若z1=z2,由于y1*N-y2*N>N,必有x1-x2>N,而x1和x2在0-N的范围内,因此这是不可能的,所以只要cpu不同,它们是不可能映射到同一虚拟地址的,也就是不会导致冲突的发生,最终通过__fix_to_virt(FIX_KMAP_BEGIN + idx)得到映射后的虚拟地址,该__fix_to_virt宏基本是一个常量转换,根据idx找到虚拟地址空间最高处的那个属于本次映射的小段。
     再看kmap_atomic这个api的原形:void *kmap_atomic(struct page *page, enum km_type type),有个参数是type,也就是说具体映射到哪一段是由调用者来决定的,可是这真的有必要吗?调用者无非需要的是一个虚拟地址而已,它不管一个page具体映射到哪个虚拟地址,就像kmap做的那样,原则上说,kmap_atomic提供的仅仅是底层的一个实现机制,一个接口,它完全可以用不同的方式实现,调用者实在没有必要牵扯进这个底层的细节问题,因此km_type是没有必要的,故而2.6.37内核中果断地去除了这个km_type,现如今2.6.37内核的kmap_atomic的实现如下:
void *kmap_atomic_prot(struct page *page, pgprot_t prot)
{
    unsigned long vaddr;
    int idx, type;
    pagefault_disable(); //原子映射是基于每cpu的,因此在当前cpu上禁用抢占,直到unmap的时候才开启,这样就不会导致原子映射的重入了,毕竟如果禁用抢占的话,调用者进程在开启抢占之前别的进程是不可能在内核空间运行,除非该进程在unmap之前睡眠,如果真的那样,别的进程就很可能在同一cpu重入kmap_atomic_prot了,然后就可能映射到同一虚拟地址(在当前2.6.37版本内部分解决了这个问题,见下面的push/pop),因此有人说原子映射期间进程不允许睡眠
    if (!PageHighMem(page)) //非高端页面直接返回一一映射地址
        return page_address(page);
    type = kmap_atomic_idx_push();  //递增一个每cpu变量,返回递增后的结果
    idx = type + KM_TYPE_NR*smp_processor_id();
    vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
    set_pte(kmap_pte-idx, mk_pte(page, prot)); //设置页表
    return (void *)vaddr;
}
static inline int kmap_atomic_idx_push(void)
{
    int idx = __get_cpu_var(__kmap_atomic_idx)++;
#ifdef CONFIG_DEBUG_HIGHMEM
    WARN_ON_ONCE(in_irq() && !irqs_disabled());
    BUG_ON(idx > KM_TYPE_NR);  //这个BUG_ON提醒__kmap_atomic_idx不能超过KM_TYPE_NR,原因同老版本的一样
#endif
    return idx;
}
可见新版本的原子映射中没有了km_type参数,只有一个page参数,完全靠一个stack实现了类似km_type的机制,在unmap的时候会调用__get_cpu_var(__kmap_atomic_idx)--将这个变量递减掉。看了这个新版本的实现之后,我们会发现,既然调用kmap_atomic_prot的时候禁用了抢占,如果进程不主动睡眠的话,在单一的cpu上__kmap_atomic_idx一般是不会大于1的,那么push中的BUG_ON当然就是杞人忧天了(除非使用一个原子映射期间又进行了另一个原子映射),然而如果原子映射的调用者睡眠了的话,谁也再来一个映射也不会和前面的重合,因为__kmap_atomic_idx此时递增了,然后这个进程也睡眠了,唤醒了原来的那个原子映射的调用者,该进程unmap了它的那个原子映射,很不巧,它释放的是第二个进程映射的页面...因此还是不要在使用原子映射之间睡眠。

     那么新版本的原子映射有没有什么缺点呢?kmap_atomic_idx_push和kmap_atomic_idx_pop都是函数,增加了几次函数调用并没有什么(它们都是inline的),最重要的是增加了几个变量的访问和操作--__kmap_atomic_idx



 本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1271144

相关文章
|
消息中间件 Java Kafka
docker-compose快速搭建kafka集群
docker-compose快速搭建kafka集群
docker-compose快速搭建kafka集群
|
Linux KVM 虚拟化
如何恢复故障KVM虚拟机qcow2磁盘镜像文件LVM分区中的数据
如何恢复故障KVM虚拟机qcow2磁盘镜像文件LVM分区中的数据
841 0
如何恢复故障KVM虚拟机qcow2磁盘镜像文件LVM分区中的数据
|
前端开发 Java 数据库
springBoot:template engine&自定义一个mvc&后端给前端传数据&增删改查 (三)
本文介绍了如何自定义一个 MVC 框架,包括后端向前端传递数据、前后端代理配置、实现增删改查功能以及分页查询。详细展示了代码示例,从配置文件到控制器、服务层和数据访问层的实现,帮助开发者快速理解和应用。
144 0
|
机器学习/深度学习 人工智能 运维
将VAE用于时间序列:生成时间序列的合成数据
变分自编码器(VAEs)是一种生成式人工智能,因其能够创建逼真的图像而备受关注,它们不仅可以应用在图像上,也可以创建时间序列数据。标准VAE可以被改编以捕捉时间序列数据的周期性和顺序模式,然后用于生成合成数据。本文将使用**一维卷积层**、**策略性的步幅选择**、**灵活的时间维度**和**季节性依赖的先验**来模拟温度数据。
239 2
将VAE用于时间序列:生成时间序列的合成数据
|
Linux 开发者 Python
从Windows到Linux,Python系统调用如何让代码飞翔🚀
【9月更文挑战第10天】在编程领域,跨越不同操作系统的障碍是常见挑战。Python凭借其“编写一次,到处运行”的理念,显著简化了这一过程。通过os、subprocess、shutil等标准库模块,Python提供了统一的接口,自动处理底层差异,使代码在Windows和Linux上无缝运行。例如,`open`函数在不同系统中以相同方式操作文件,而`subprocess`模块则能一致地执行系统命令。此外,第三方库如psutil进一步增强了跨平台能力,使开发者能够轻松编写高效且易维护的代码。借助Python的强大系统调用功能,跨平台编程变得简单高效。
259 1
|
机器学习/深度学习 存储 人工智能
图搜索算法详解
【5月更文挑战第11天】本文介绍了图搜索算法的基础知识,包括深度优先搜索(DFS)、广度优先搜索(BFS)和启发式搜索(如A*算法)。讨论了图搜索中的常见问题、易错点及避免方法,并提供了BFS和A*的Python代码示例。文章强调了正确标记节点、边界条件检查、测试与调试以及选择合适搜索策略的重要性。最后,提到了图搜索在路径规划、游戏AI和网络路由等领域的应用,并概述了性能优化策略。
445 3
|
机器学习/深度学习 Python
深入了解CatBoost:自定义目标函数与度量的高级教程
深入了解CatBoost:自定义目标函数与度量的高级教程【2月更文挑战第18天】
658 1
|
缓存 JavaScript 前端开发
一个大四学长分享自己的web前端学习路线--vue篇(1/3)
学会使用插值、什么是表达式、了解什么是指令、什么是动态属性已经v-html标签(注意:v-html这个标签会有XSS风险,它会将我们的子组件覆盖掉)
36839 1
一个大四学长分享自己的web前端学习路线--vue篇(1/3)
|
监控 安全 Unix
Rsync+Inotify实现数据实时同步
Rsync+Inotify实现数据实时同步
|
C语言
使用buildroot制作根文件系统(龙芯1B使用)
使用buildroot制作根文件系统(龙芯1B使用)
721 0
使用buildroot制作根文件系统(龙芯1B使用)