当前位置: 代码网 > 服务器>服务器>Linux > 驱动开发系列07 - 驱动程序如何分配内存

驱动开发系列07 - 驱动程序如何分配内存

2024年08月03日 Linux 我要评论
Linux 内核提供了丰富的内存分配函数、在本文中,我们将介绍在设备驱动程序中分配和使用内存的方法,以及如何优化系统的内存资源。由于内核为驱动程序提供了统一的内存管理接口。所以我们不会去讨论不同架构是如何管理内存的,文本不涉及分段、分页等问题,此外在本文中,我们也不会描述Linux内存管理的内部细节,这在驱动开发系列06 - 内存映射和DMA已经介绍过了,感兴趣的读者可以前往了解。kmalloc 是一个功能强大的内存分配函数,由于它与 malloc 相似,因此很容易学习。

目录

一:概述

二:kmalloc 介绍

1.参数介绍 

2.内存区域(memory zones)

3. size 对齐

二:后备缓存(lookaside caches)

1. 内存池

三:get_free_page 介绍

1. alloc_pages 接口介绍

四:vmalloc 介绍

五:per-cpu 变量介绍

六:分配大量连续内存的方法


一:概述

        linux 内核提供了丰富的内存分配函数、在本文中,我们将介绍在设备驱动程序中分配和使用内存的方法,以及如何优化系统的内存资源。由于内核为驱动程序提供了统一的内存管理接口。所以我们不会去讨论不同架构是如何管理内存的,文本不涉及分段、分页等问题,此外在本文中,我们也不会描述linux内存管理的内部细节,这在驱动开发系列06 - 内存映射和dma已经介绍过了,感兴趣的读者可以前往了解。

二:kmalloc 介绍

        kmalloc 是一个功能强大的内存分配函数,由于它与 malloc 相似,因此很容易学习。该函数执行速度很快(除非被阻塞),而且不会清除获取的内存; 分配的区域仍保留其先前的内容。分配的区域在物理内存中也是连续的。在接下来的几节中,我们将详细介绍 kmalloc,这样你就可以将它与后面讨论的内存分配技术进行比较。

        kmalloc 的函数原型是:

#include <linux/slab.h>
void *kmalloc(size_t size, int flags);
1.参数介绍 

        kmalloc 的第一个参数是待分配内存的大小,以字节为单位。第二个参数(分配标志)更有意思,因为它可以通过多种方式控制 kmalloc 的行为。

        最常用的标志是 gfp_kernel,用于在内核空间分配内存。分配操作内部通过最终调用 __get_free_pages实现的,这是 gfp_ 前缀的来源。使用gfp_kernel分配内存,这也意味着该函数调用其实是在当前进程上执行的系统调用。当在内存不足的情况下调用 kmalloc 时,可以让当前进程休眠,等待由空闲的页面。因此,使用 gfp_kernel 分配内存的函数必须是可重入的,并且不能在原子上下文中调用(不能睡眠)。在当前进程休眠时,内核会采取适当的措施来找到一些空闲内存,方法是将缓存刷新到磁盘或从用户进程中交换内存。

        gfp_kernel 并不总是被正确使用;有时 kmalloc 会在进程的上下文之外被调用。例如,这种情况可能发生在中断处理程序、tasklets、内核定时器中。在这种情况下,当前进程不应进入休眠状态,驱动程序应使用 gfp_atomic 标志,而不是gfp_kernel。使用 gfp_atomic 进程不会睡眠。

        内核通常会尝试保留一些空闲页面,以实现原子分配(gfp_atomic)。 当使用gfp_atomic 时,kmalloc 甚至可以使用最后一个空闲页面。但如果最后一页不存在,分配就会失败。

        gfp_kernel 和 gfp_atomic是设备驱动程序经常使用的,除此之外还有gfp_dma等。所有flag都在 <linux/gfp.h> 中定义。

2.内存区域(memory zones)

        总所周知,linux中将内存分为内核空间和用户空间,比如以x86为例,32位系统上可寻址内存地址范围是4g(内核空间占1gb,用户空间占3gb)。 而内核空间(1gb)又进一步分为三个内存区域,它们分别是dma_zone(16m), normal_zone(896m), high_zone(128m)。

        一般的内存分配通常都在normal_zone区域内,设置区域标志位将决定内存从哪个区域中分配。我们的想法是,每一个必须了解每个区域的内存范围以及它们的含义。

         dma_zone 区域是一个特殊地址范围,外设可在该范围内执行 dma 操作。在大多数正常平台上,这个区域包含整个内存范围。在 x86 平台上,只有内存的前16 mb 属于这个区域,因为传统的 isa 设备只可在 16m 区域内执行 dma,pci 设备则无此限制。

        high_zone区域高内存是一种机制,在32位平台上,允许内核能够访问更多的内存(1gb以外的内存)。通常在这些内存在没有建立特殊映射之前,不能被内核直接访问并且很难被使用。如果你的驱动需要使用大量的内存,在high_zone可用的情况下,最好使用high_zone区域。它将在大型系统上工作得更好。

        如果为内存分配请求分配一个新页时,内核会建立一个用于搜索内存页的区域列表。如果指定 __gfp_dma,那只会在dma_zone区域搜索可用的页,如果该区域没有可用的内存时,分配会失败。如果没有显示指定区域标志(flag),那么会在 dma_zone 和 normal_zone 区域中搜索可用的页;如果指定 __gfp_highmem,那么会在dma_zone, normal_zone, high_zone三个区域内搜索。(注意:kmalloc 不能分配high_zone内存)。

        在非统一内存访问(numa)的系统上,情况要复杂一些。作为一般性原则,分配器通常去尝试分配处理器的本地内存,尽管有一些方法可以改变这种行为。

        内存区域背后的机制在mm/page_alloc.c中实现,而区域的初始化依赖特定于平台,比如 arch在mm/init.c中。

3. size 对齐

        内核以页为单位管理系统物理内存,因此,kmalloc与用户空间malloc的实现由很大不同,例如,采用面向堆栈的技术将很快遇到麻烦,因为很难绕过页的边界的限制。因此,内核使用了一个特殊的面向页的分配技术来更好的使用系统内存。

        linux通过创建一组固定大小的内存对象池来处理内存分配。处理分配请求的方式是从池子中获取一个足够大的内存对象,并且将整个内存块返回给请求者。这个内存管理策略是相当复杂的,并且对驱动开发者来说,内存管理的实现细节也不是很有趣,通常不需要关心它。

        但是驱动开发人员需要记住一件事,尽管内核只分配预定义固定大小的字节数组,如果你请求分配任意大小的内存,你可能会得到比你所请求略微多一点的内存,最多是多两倍。不过,程序员应该记住, kmalloc 可以处理的最小内存分配是 32 或 64 字节,依赖于系统架构所使用的页面大小。

        kmalloc 可分配的内存大小也有一个上限,这依赖于系统架构和内核的配置。如果你的代码是想可以完全跨平台移植的,那么不能分配内存大小不能超过128kb,如果你需要比几千个字节还多的内存,显然,还有比kmalloc更适合的方法,这将在后面介绍。

二:后备缓存(lookaside caches)

        一个驱动程序经常重复分配许多相同大小的内存对象。假设内核已经维护了一组内存对象池,这些内存对象大

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com