前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >内存调试的相关分析

内存调试的相关分析

作者头像
刘盼
发布2018-07-26 14:52:43
1.7K0
发布2018-07-26 14:52:43
举报
文章被收录于专栏:人人都是极客人人都是极客

DMA和Cache的一致性

我们知道外设访问内存需要通过DMA进行数据搬移,关于cpu, cache, device, dma, memory的关系可以通过下图说明:

但是在开发过程中我们经常遇到以下两种问题:

  • DMA传输数据到memory, cache中有可能是老的数据
  • CPU写数据到memory,cache中是新数据,memory是老数据

DMA一般没有MMU,访问不到CPU内部的cache,所以在做DMA编程时会遇到cache一致性问题。

如下图:假设MEMORY里面有一块红色的区域,并且CPU读过它,于是红色区域也进CACHE。

但是,假设现在 DMA 把外设的一个白色搬移到了内存原本红色的位置,如下图:

这个时候,内存虽然白了,CPU 读到的却还是红色,因为 CACHE 命中了,这就出现了 cache 的不一致。

当然,如果是 CPU 写数据到内存,它也只是先写进 cache(不一定进了内存),这个时 候如果做一个内存到外设的 DMA 操作,外设可能就得到错误的内存里面的老数据。也 会造成另一个方向的 cache 不一致。

所以最简单方法,自然是让 CPU 访问 DMA buffer 的时候也不带 cache。

当硬件上不带硬件 cache 同步单元时,cache 一致性的解决方法,在 Linux 中主要有两 类 API,如下图:

Coherent DMA buffers的原理是,当编写驱动代码时,可以先申请用作DMA传输的内存,再把内存映射成不带cache的。事实上,缺省情况下,dma_alloc_coherent()申请的内存是进行uncache配置的。

一般在做嵌入式驱动编程,用dma_alloc_coherent申请内存时,都会映射到CMA区域。下图中__alloc_from_contiguous函数就是调用dma_alloc_coherent时,最终从CMA区域申请内存的代码。arch/arm/mm/dma-mapping.c

但是,由于现代SoC特别强,有一些SoC里面可以用硬件做CPU和外设的cache coherence,如图中的cache coherent interconnect:

这些SoC的厂商就可以把内核的通用实现overwrite掉,变成dma_alloc_coherent()申请的内存也是可以带cache的。全部由硬件的cache coherent interconnect来做硬件的cache一致性同步。此时,DMA可以直接访问带cache的内存。

硬件上如果有 IOMMU,dma_alloc_coherent()则不再需要从 CMA 区域申请连续的内存。 IOMMU 会把零散的物理页建立一个和 CPU 里一样的页表,然后再把这些零散的物理页 映射成对于 DMA 引擎看起来连续的虚拟页。DMA 引擎就可以访问非连续的物理地址。

DMA Streaming Mapping适用于在写驱动时,用作DMA的内存不是由自己分配,而是上层接口提供。比如TCP/IP的报文,直接让你去做DMA。再比如文件系统提供一个地址,让你把硬盘里的东西直接搬移到提供的地址中,文件系统提供的内存肯定是带cache且不带cache同步,不具备cache一致性的。此时,在做DMA操作之前必须调用dma_map_sg或dma_map_single(区别是看DMA引擎是否支持散列操作,如果支持可以用dma_map_sg)。

dma_map_sg和dma_map_single函数都有一个表明方向的参数,代表从内存到外设,还是从外设到内存。当从内存到外设时,一般Linux内核会自动做cache flush,以保证做DMA传输时可以从内存中取到最新的数据。相反,当从外设到内存时,会做cache的invalid动作。

dma_map_single与dma_unmap_single之间只允许DMA传输,不允许CPU干预。原因是比如调用dma_map_single从内存到外设,Linux内核已经完成cache flush动作,此时如果允许CPU参与,则有可能CPU将cache写得再次比memory新。

简单来说,做内核开发的原则是,若用作 DMA 的内存是自己申请,不是上层提供,则 只调用前端的 dma_alloc_coherent,不用太考虑底层具体操作,如下图所示:

如果是上层提供,则调用 dma_map_sg 或着 dma_map_single。各个 IC 公司会帮你搞定 底层具体实现,底层的具体实现细节每个芯片都不一样。

附注:

i. cache 写机制:

Write-through(直写模式)在数据更新时,同时写入 cache 和 memory。此模式的优点是操作简单;缺点是因为数据修改需要同时写入 memory,性能差。Write-back(回写模式)在数据更新时只写入 cache。只在数据被替换出 cache 时 (cpu 内部硬件采用 LRU 算法),被修改的 cache 数据才会被写到 memory。此模式的优点是数据写入速度快,因为不需要写 memory;缺点是一旦更新后的数据未被写入 memory 时出现系统掉电的情况,数据将无法找回。

ii. 页表中除了有虚拟地址到物理地址的对应关系,RWX 权限,user/kernel 权限, 还会记录 cache 特性。

Memory CGroup

首先,Linux在进行内存回收(memory reclaim)时,/proc/sys/vm/swappiness设置的越大,越倾向于回收匿名页;swappiness越小,越倾向于回收file-backed的页面。算法都是LRU。

在使能Memory CGroup的情况下,每个memory group可以设置自己的swappiness值。在/sys/fs/cgroup/memory中创建目录,然后把一些进程加入group,就可以通过修改group的swappiness值,控制一组进程的内存回收倾向。

还可以控制一个group的最大内存消耗,达到最大消耗则发生OOM。演示如下:

在/sys/fs/cgroup/memory目录下创建子目录A,目录中自动生成配置文件。echo 20M到内存最大消耗限制配置文件中,编译oom.c程序,将其放入group A中执行:

申请到20M时即被杀死,可以dmesg查看具体信息。

脏页写回

Linux 中脏页不能太多也不能存在太久,原因有突然掉电丢失,给后面的硬盘操作带来 很大压力等。Linux 中有一个机制可以控制脏页写回。 从时间和空间(比例)两方面控制: 时间,dirty_expire_centisecs,dirty_writeback_centisecs(centisecs,百分之一秒) Linux 有一个后台的 kernel flusher 线程,这个线程会以 dirty_writeback_centisecs 的周期 自启动。当脏页时间到达 dirty_expire_centisecs 就写回(只看脏页时间,不看脏页数量)。

空间,dirty_ratio,dirty_background_ratio 当脏页在内存中的比例到达一定程度,就必须要写回。

dirty_background_ratio 一旦达到,后台的线程就开始写脏页了,但可能来不及写,因为 在写脏页的时候,应用程序可能也在疯狂的调用write,这时候如果达到dirty_ratio门限, 前台的进程就会被直接堵住,必须要等脏页写回。脏页数量在 dirty_background_ratio 和 dirty_ratio 之间,应用程序则不会被堵住。

附注: 脏页-linux 内核中的概念,因为硬盘的读写速度远赶不上内存的速度,系统就把读写比 较频繁的数据事先放到内存中,以提高读写速度,称为高速缓存。linux 是以页作为高速 缓存的单位,当进程修改了高速缓存里的数据时,该页就被内核标记为脏页,内核将会 在合适的时间把脏页的数据写到磁盘中去,以保持高速缓存中的数据和磁盘中的数据是 一致的。

水位控制:内存何时开始回收

min,low,high 三个水位,都是由/proc/sys/vm/min_free_kbytes 文 件 控 制 。 min_free_kbytesde 默认值如上图中的计算公式所示。 一般内存越大预留的内存也越多。预留内存的原因是系统中需要预留一些紧急内存给某 些进程,比如回收内存的线程 kswapd 也是需要内存的,这类进程会设置 PF_MEMALLOC 标志。

min_free_kbytes 决定了最低水位 min,low=min * 5/4,high=min * 3/2。 当一个 ZONE 里的内存达到 low, linux 就开始后台回收内存,直到 ZONE 里的内存达到 high 水位。但应用程序可能在更快的申请内存,这时空闲内存就有可能达到 min 水位, 一旦达到,应用程序的写内存操作就会被阻塞,直接在应用程序的进程上下文中进行回 收(direct reclaim)。如下图所示:

swappiness大小的设定需要根据具体情况

推荐阅读:

CPU是如何访问内存的?

物理地址和虚拟地址的分布

Linux内核内存管理算法Buddy和Slab

Linux用户态进程的内存管理

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-05-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 人人都是极客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • DMA和Cache的一致性
  • Memory CGroup
  • 脏页写回
  • 水位控制:内存何时开始回收
  • swappiness大小的设定需要根据具体情况
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档