tag 标签: Linux内核开发

相关博文
  • 热度 4
    2023-10-24 12:27
    1823 次阅读|
    0 个评论
    Linux内存管理 | 四、物理地址空间设计模型 前面几篇文章,主要讲解了虚拟内存空间的布局和管理,下面同步来聊聊物理内存空间的布局和管理。 1、物理内存 什么是物理内存? 我们平时聊的内存,也叫随机访问存储器(random-access memory),也叫RAM。 RAM分为两类: SRAM:静态RAM,其主要用于CPU高速缓存 L1Cache,L2Cache,L3Cache,其特点是访问速度快,访问速度为 1 - 30 个时钟周期,但是容量小,造价高。 CPU缓存结构.png DRAM:动态RAM,其主要用于我们常说的主存上,其特点的是访问速度慢(相对高速缓存),访问速度为 50 - 200 个时钟周期,但是容量大,造价便宜些(相对高速缓存)。 image.png DRAM经过组合起来,就作为我们的计算机内存,也是物理内存。 2、物理内存访问模型 上面介绍了物理内存的基本组成,那么CPU是如何访问物理内存的呢? 对于CPU访问物理内存,Linux提供了两种架构:UMA(Uniform Memory Access)一致内存访问,NUMA(Non-Uniform Memory Access)非一致内存访问。 2.1 UMA 在UMA架构下,多核处理器中的多个CPU,位于总线的一侧,所有的内存条组成的物理内存位于总线的另一侧。 所有的CPU访问内存都要经过总线,并且距离都是一样的,所以在UMA架构下, 所有CPU具有相同的访问特性,即对内存的访问具有相同的速度。 image-20231013075142500 2.2 NUMA 这种架构,系统中的 各个处理器都有本地内存 ,处理器与处理器之间也通过总线连接,以便于其他处理器对本地内存的访问。 与UMA不同的是,处理器访问本地内存的速度要快于对其他处理器本地内存的访问。 image-20231013074823586 3、物理内存组织模型 内存页是物理内存管理中最小单位,有时也成为页帧(Page Frame)。 内核对物理内存划分为一页一页的连续的内存块,每页大小4KB,并且使用struct page结构体来表示页结构,其中封装了每个页的状态信息,包括:组织结构,使用信息,统计信息等。 page结构体较为复杂,我们后续再深入了解。 顺便介绍一下 我的圈子: 高级工程师聚集地 ,期待大家的加入。 3.1 FLATMEM平坦内存模型 FLATMEM即:flat memory model。 我们把物理内存想象成它是由连续的一页一页的块组成的,我们从0开始对物理页编号,这样每个物理页都会有页号。 由于物理地址是连续的,页也是连续的,每个页大小也是一样的。因而对于任何一个地址,只要直接除一下每页的大小,很容易直接算出在哪一页。 如果是这样,整个物理内存的布局就非常简单、易管理,这就是最经典的 平坦内存模型 (Flat Memory Model)。 image.png 如上图,平坦内存模型中,内核使用一个mem_map的全局数组,来组织所有划分出来的物理内存页,下标由PFN表示。 在平坦内存模型下 ,page_to_pfn 与 pfn_to_page 的计算逻辑就非常简单,本质就是基于 mem_map 数组进行偏移操作。 # ifndef ARCH_PFN_OFFSET # define ARCH_PFN_OFFSET(0UL) # endif # if defined(CONFIG_FLATMEM) # define __pfn_to_page(pfn)(mem_map+((pfn)-ARCH_PFN_OFFSET)) # define __page_to_pfn(page)((unsignedlong)((page)-mem_map)+ARCH_PFN_OFFSET) # endif ARCH_PFN_OFFSET 是 PFN 的起始偏移量。 3.2 DISCONTIGMEM 不连续内存模型 DISCONTIGMEM即:discontiguous memory model。 我们早期内核使用的是FLATMEM模型,该模型对于较小的,连续的物理空间是方便使用的,但是当物理内存不连续时,使用mem_map管理,就会出现空洞,这会浪费mem_map数组本身占用的内存空间。 image.png 对于NUMA访问内存模型,物理内存分布就是不连续的,为了有效管理,DISCONTIGMEM 不连续内存模型出现了。 image.png 在不连续的物理内存中,DISCONTIGMEM不连续内存模型,将物理内存分成了一个个的node,然后每个node管理一块连续的物理内存,连续的物理内存仍然使用FLATMEM平坦内存模型来管理,从而避免了内存空洞的浪费。 我们可以看出 DISCONTIGMEM 非连续内存模型其实就是 FLATMEM 平坦内存模型的一种扩展。 DISCONTIGMEM是个稍纵即逝的内存模型,在SPARSEMEM出现后即被完全替代 。 3.3 SPARSEMEM稀疏内存模型 随着内存技术的发展,内核可以支持物理内存的热插拔了(像我们的内存条,可以直接插入拔出),这样不连续物理内存已然称为常态。 SPARSEMEM稀疏内存模型的核心思想就是 对粒度更小的连续内存块进行精细的管理 ,用于管理连续内存块的单元被称作 section 。 物理页大小为 4k 的情况下, section 的大小为 128M ,物理页大小为 16k 的情况下, section 的大小为 512M。 在内核中,使用struct mem_section结构体表示SPARSEMEM模型中的section struct mem_section { unsigned long section_mem_map; ... } 每个mem_section管理一片小的,物理内存连续的区域,并且支持对该区域的offline/online状态 所有的mem_section都保存在一个全局数组中 整体的框架如下: image.png 在 SPARSEMEM 稀疏内存模型下 page_to_pfn 与 pfn_to_page 的计算逻辑又发生了变化。 # if defined(CONFIG_SPARSEMEM) /* *Note:section'smem_mapisencodedtoreflectitsstart_pfn. *section .section_mem_map==mem_map'saddress-start_pfn; */ # define __page_to_pfn(pg)\ ({conststructpage*__pg=(pg);\ int__sec=page_to_section(__pg);\ (unsignedlong)(__pg-__section_mem_map_addr(__nr_to_section(__sec)));\ }) # define __pfn_to_page(pfn)\ ({unsignedlong__pfn=(pfn);\ structmem_section*__sec=__pfn_to_section(__pfn);\ __section_mem_map_addr(__sec)+__pfn;\ }) # endif 在 page_to_pfn 的转换中,首先需要通过 page_to_section 根据 struct page 结构定位到 mem_section 数组中具体的 section 结构。然后在通过 section_mem_map 定位到具体的 PFN。 在 pfn_to_page 的转换中,首先需要通过 __pfn_to_section 根据 PFN 定位到 mem_section 数组中具体的 section 结构。然后在通过 PFN 在 section_mem_map 数组中定位到具体的物理页 Page 。 4、总结 以上,我们先对物理内存空间有一个基础的了解,明白物理内存空间的内存访问模型和组织模型,下面我们再详细介绍物理内存空间的布局和管理。
  • 热度 4
    2023-10-4 13:32
    957 次阅读|
    0 个评论
    Linux内存管理 | 一、内存管理的由来及思想 1、前言 《中庸》有:“九层之台,起于垒土” 之说,那么对于我们搞技术的人,同样如此! 对于Linux内存管理,你可以说没有留意过,但是它存在于我们日常开发的方方面面,你所打开的文件,你所创建的变量,你所运行的程序,无不以此为基础,它可以说是操作系统的基石;只是底层被封装的太好了,以至于我们在做开发的过程中,不需要关心的太多,哪有什么岁月静好,只是有人在负重前行罢了。 虽然日常开发中涉及的比较少,但是作为一个合格的Linux开发者,搞懂内存管理,又显得至关重要,同时也会对嵌入式开发大有脾益,今天我们就来详细聊聊内存管理的那点事。 该方面的文章,网上也有很多写的非常不错,但是100个人有100种理解方式,并且不同的人,基础不同,理解能力也不同,所以我写这系列的文章,也更有了意义。 2、内存管理的由来 为什么要有这个概念呢? 首先,内存管理,管理的是个什么东西? 管理的其实是我们的物理内存,也就是我们的RAM空间,在电脑上,表现为我们安装的内存条,有的人装个4G的、8G的、甚至64G的,这些就是实打实的物理空间大小,也就是我们的实际的硬件资源。 img 为什么要进行管理? 做嵌入式的都知道,像我们刚开始玩的C51单片机、STM32单片机,我们将程序烧录到Flash中后,开机启动后,然后CPU会将Flash程序加载到RAM中,也就是我们的物理内存,随后我们的所有操作都是基于这一个物理内存所进行的。 img 那么此时 : 我们想再次运行一个一模一样的程序怎么办? 即使运行了,那两个程序同时操作了同一个变量,值被错误修改了怎么办? 这些就是Linux内存管理要做的事情。 顺便介绍一下 我的圈子: 高级工程师聚集地 ,期待大家的加入。 3、Linux内存管理思想 为了解决上面的一些问题,Linux采用虚拟内存管理技术。 Linux操作系统抽象出来一个 虚拟地址空间 的概念,供上层用户使用,这么做的目的是为了让多个用户进程,都以为自己独享了内存空间。 而虚拟地址空间与物理地址空间的对应关系,就交给了一个MMU(Memory Managerment Unit)的家伙来管理,其主要负责将虚拟内存空间映射到真实的物理地址空间。 img 这么做的主要目的在于: 让每个进程都拥有相同大小的虚拟地址空间 避免用户直接访问物理内存,导致系统崩溃 这样,我们同时执行多个进程,虽然看起来虚拟地址操作都是相同的,但是通过MMU之后,就被映射到了不同的物理地址空间,这样就解决了以上的问题。 4、总结 熟悉了内存管理由来以及其思想,我们可以看出,操作系统的内存管理,主要分为以下几个方面: 虚拟内存空间管理 :我们抽象出来的虚拟地址空间,该怎么使用,该怎么管理? 物理内存空间管理 :虚拟地址映射到物理内存空间后,该如何管理,如何分配? 如何映射 :虚拟内存如何映射到物理内存,是怎么操作的,映射方法有哪些? 下面我们来一一详细探究。
  • 热度 5
    2023-8-30 09:36
    1702 次阅读|
    0 个评论
    如何写一个外设驱动?1、编写外设驱动流程 img 该问题为基础问题,也是大家基本功的表现,下面一起来看一下吧 写一个常见的字符设备外设驱动,主要遵循以下流程 : 1. 了解硬件和接口 :查看外设用户手册,了解该外设使用的是什么通信接口,IIC、SPI、UART等。 2. 设备树配置 :遵循设备树语法,在设备树中添加该外设的设备属性信息。 3. 外设driver结构体配置 :遵循Linux驱动框架,创建外设driver结构体,并设定相应的回调函数,包括probe、remove、外设driver和device的匹配规则id_table,驱动名称等信息。 4. device和driver匹配 :通过insmod来将外设驱动加载到总线上,按照定义好的匹配规则,触发probe函数。 5. probe实现 :创建字符设备,以供用户层使用。字符设备创建流程如下: 1.分配字符设备的主设备号和次设备号,并为字符设备创建内存空间。 2.关联字符设备结构体与文件操作结构体ops,创建open、read、write函数 3.添加字符设备到虚拟文件系统中,包括:profs和sysfs 4.将外设驱动注册进入子系统中,实现统一管理。 6. 完善文件操作接口 :open、read、write为暴漏在用户层的文件操作接口,我们一般通过该接口实现对外设的功能操作。 1.open函数:一般用于初始化外设 2.read函数:一般用于用户层读取外设信息,通过调用copy_to_user将数据搬运到用户空间 3.write函数:一般用于用户层向外设写入数据,通过调用copy_from_user将用户数据搬运到内核层。 7. 卸载驱动 :rmmod驱动后,会调用remove接口负责卸载驱动,包括:sysfs、profs的节点删除,字符设备结构体的注销,外设驱动结构体的注销。 以上就是一个简单的外设驱动的编写流程,示例外设驱动程序以及更多嵌入式知识可以看这里: 传送门 2、常见问题 img Q:设备编号的分配方式有哪些? A:设备号的分配方式主要有两种:动态分配和静态分配。 •动态分配:系统自动为其分配一个未被占用的设备号 •静态分配:自行定义设备号,注册进入系统中。 Q:主设备号和次设备号有什么意义? A:设备号是用来标识设备的关键信息,主设备号用于标识设备的类型,次设备号用于区分同类型的不同设备。 Q:设备树匹配方式有哪些? A:device和driver匹配方式有4种,如下: • 设备树(OF)匹配方式 :通过设备树中的compatible属性匹配 • ACPI匹配方式 :ACPI(Advanced Configuration and Power Interface),通过设备ID编号和驱动ID编号来匹配 • ID Table匹配方式 :驱动提供ID Table,设备提供一个ID变量进行匹配 • Name匹配方式 :通过比较设备名称和驱动名称来进行匹配。 img
  • 热度 3
    2023-8-10 12:42
    1498 次阅读|
    0 个评论
    为什么 Linux 内核中不经常使用 typedef? 为什么 Linux 内核中不经常使用 typedef 我们在进行Linux驱动开发过程中,有没有出现过这样的报错? WARNING: do notaddnewtypedefs 不允许使用typedef! 虽然只是一个警告,但是如果你想往开源仓库提交代码,这就是一个必优化项。 那么,为什么Linux内核不建议使用typedef呢? 1、Linus Torvalds 的态度 On Mon, 10 Jun 2002, Linus Torvalds wrote: --snip/snip But in the end, maintainership matters. I personally don't want the typedef culture to get the upper hand, but I don't mind a few of them, and people who maintain their own code usually get the last word. to sum it up: using the "struct mystruct" is recommended , but not a must. Torvalds 本人不太想看到typedef文化占上风,但是维护自己代码的人通常有最后的发言权。 Torvalds 还是比较推荐使用struct mystruct的结构 不易理解 :使用typedef类型,不容易去理解变量的实际类型是什么样子的 不好维护 :由于Linux内核架构的庞大,不同架构之间定义的typedef类型可能并不具有通用性。 Torvalds 原文详见:https://lkml.indiana.edu/hypermail/linux/kernel/0206.1/0402.html 2、内核编码规范 从内核编码规范的角度,来看typedef 内核编码规范给出了typedef使用的一些场合: 完全不透明的对象:隐藏内部对象 明确的整数类型:抽象有助于避免混淆是int型还是long型,如u8/u16/u32 在某些特殊情况下,与标准C99类型相同的新类型。 可在用户空间中使用的类型 内核编码规范详见:https://www.kernel.org/doc/html/v4.10/process/coding-style.html 3、个人看法 个人感觉,从大型项目的开发维护上来说,typedef不建议使用,避免造成类型泛滥,也更加不容易理解。 对于个人开发的小项目,typedef可以完全看自己心情,毕竟typedef褒贬不一。 下面分享一些社区讨论帖子: 为什么我们要在C语言中频繁使用typedef:https://stackoverflow.com/questions/252780/why-should-we-typedef-a-struct-so-often-in-c 为什么Linux编码锋哥不建议使用typedef:https://www.reddit.com/r/C_Programming/comments/dan8vr/why_does_the_linux_kernel_coding_style_guide/?rdt=36702 ▐ 我的圈子: 高级工程师聚集地