1.Linux内核源码解析---万字解析从设计模式推演per-cpu实现原理
2.linux源码解读(三十二):dpdk原理概述(一)
3.深入理解Linux内存管理(九)大页
4.Linux内核源码分析:Linux进程描述符task_ struct结构体详解
5.如何从官网获取各个版本Linux内核的源页源码
6.Linux HugePage
Linux内核源码解析---万字解析从设计模式推演per-cpu实现原理
引子
在如今的大型服务器中,NUMA架构扮演着关键角色。码多它允许系统拥有多个物理CPU,源页不同NUMA节点之间通过QPI通信。码多虽然硬件连接细节在此不作深入讨论,源页但需明白每个CPU优先访问本节点内存,码多自然语音识别源码当本地内存不足时,源页可向其他节点申请。码多从传统的源页SMP架构转向NUMA架构,主要是码多为了解决随着CPU数量增多而带来的总线压力问题。
分配物理内存时,源页numa_node_id() 方法用于查询当前CPU所在的码多NUMA节点。频繁的源页内存申请操作促使Linux内核采用per-cpu实现,将CPU访问的码多变量复制到每个CPU中,以减少缓存行竞争和False Sharing,源页类似于Java中的Thread Local。
分配物理页
尽管我们不必关注底层实现,buddy system负责分配物理页,关键在于使用了numa_node_id方法。接下来,我们将深入探索整个Linux内核的per-cpu体系。
numa_node_id源码分析获取数据
在topology.h中,我们发现使用了raw_cpu_read函数,传入了numa_node参数。接下来,我们来了解numa_node的定义。
在topology.h中定义了numa_node。我们继续跟踪DECLARE_PER_CPU_SECTION的定义,最终揭示numa_node是一个共享全局变量,类型为int,存储在.data..percpu段中。efucms源码
在percpu-defs.h中,numa_node被放置在ELF文件的.data..percpu段中,这些段在运行阶段即为段。接下来,我们返回raw_cpu_read方法。
在percpu-defs.h中,我们继续跟进__pcpu_size_call_return方法,此方法根据per-cpu变量的大小生成回调函数。对于numa_node的int类型,最终拼接得到的是raw_cpu_read_4方法。
在percpu.h中,调用了一般的read方法。在percpu.h中,获取numa_node的绝对地址,并通过raw_cpu_ptr方法。
在percpu-defs.h中,我们略过验证指针的环节,追踪arch_raw_cpu_ptr方法。接下来,我们来看x架构的实现。
在percpu.h中,使用汇编获取this_cpu_off的地址,代表此CPU内存副本到".data..percpu"的偏移量。加上numa_node相对于原始内存副本的偏移量,最终通过解引用获得真正内存地址内的值。
对于其他架构,实现方式相似,通过获取自己CPU的偏移量,最终通过相对偏移得到pcp变量的地址。
放入数据
讨论Linux内核启动过程时,mojang源码我们不得不关注per-cpu的值是如何被放入的。
在main.c中,我们以x实现为例进行分析。通过setup_percpu.c文件中的代码,我们将node值赋给每个CPU的numa_node地址处。具体计算方法通过early_cpu_to_node实现,此处不作展开。
在percpu-defs.h中,我们来看看如何获取每个CPU的numa_node地址,最终还是通过简单的偏移获取。需要注意如何获取每个CPU的副本偏移地址。
在percpu.h中,我们发现一个关键数组__per_cpu_offset,其中保存了每个CPU副本的偏移值,通过CPU的索引来查找。
接下来,我们来设计PER CPU模块。
设计一个全面的PER CPU架构,它支持UMA或NUMA架构。我们设计了一个包含NUMA节点的结构体,内部管理所有CPU。为每个CPU创建副本,其中存储所有per-cpu变量。静态数据在编译时放入原始数据段,动态数据在运行时生成。
最后,我们回到setup_per_cpu_areas方法的分析。在setup_percpu.c中,我们详细探讨了关键方法pcpu_embed_first_chunk。此方法管理group、54源码unit、静态、保留、动态区域。
通过percpu.c中的关键变量__per_cpu_load和vmlinux.lds.S的链接脚本,我们了解了per-cpu加载时的地址符号。PERCPU_INPUT宏定义了静态原始数据的起始和结束符号。
接下来,我们关注如何分配per-cpu元数据信息pcpu_alloc_info。percpu.c中的方法执行后,元数据分配如下图所示。
接着,我们分析pcpu_alloc_alloc_info的方法,完成元数据分配。
在pcpu_setup_first_chunk方法中,我们看到分配的smap和dmap在后期将通过slab再次分配。
在main.c的mm_init中,我们关注重点区域,完成map数组的slab分配。
至此,我们探讨了Linux内核中per-cpu实现的原理,从设计到源码分析,全面展现了这一关键机制在现代服务器架构中的作用。
linux源码解读(三十二):dpdk原理概述(一)
Linux源码解析(三十二):深入理解DPDK原理(一)
几十年来,随着技术的发展,传统操作系统和网络架构在处理某些业务需求时已显得力不从心。为降低修改底层操作系统的高昂成本,人们开始在应用层寻求解决方案,如协程和QUIC等。然而,ep源码一个主要问题在于基于内核的网络数据IO,其繁琐的处理流程引发了效率低下和性能损耗。
传统网络开发中,数据收发依赖于内核的receive和send函数,经过一系列步骤:网卡接收数据、硬件中断通知、数据复制到内存、内核线程处理、协议栈层层剥开,最终传递给应用层。这种长链式处理方式带来了一系列问题,如上下文切换和协议栈开销。
为打破这种限制,Linux引入了UIO(用户空间接口设备)机制,允许用户空间直接控制网卡,跳过内核协议栈,从而大大简化了数据处理流程。UIO设备提供文件接口,通过mmap映射内存,允许用户直接操作设备数据,实现绕过内核控制网络I/O的设想。
DPDK(Data Plane Development Kit)正是利用了UIO的优点,如Huge Page大页技术减少TLB miss,内存池优化内存管理,Ring无锁环设计提高并发性能,以及PMD poll-mode驱动避免中断带来的开销。它采用轮询而非中断处理模式,实现零拷贝、低系统调用、减少上下文切换等优势。
DPDK还注重内存分配和CPU亲和性,通过NUMA内存优化减少跨节点访问,提高性能,并利用CPU亲和性避免缓存失效,提升执行效率。学习DPDK,可以深入理解高性能网络编程和虚拟化领域的技术,更多资源可通过相关学习群获取。
深入了解DPDK原理,可以从一系列资源开始,如腾讯云博客、CSDN博客、B站视频和LWN文章,以及Chowdera的DPDK示例和腾讯云的DPDK内存池讲解。
源:cnblogs.com/thesevenths...
深入理解Linux内存管理(九)大页
在前文概述了伙伴系统和slab缓存后,本节深入探讨Linux大页机制。让我们首先从硬件基础谈起。
在操作系统使用大页功能时,硬件支持至关重要。在Intel手册中,位系统中,若CR4.PSE标志和PDE的PS标志均激活,:0位用于页内偏移,4M大小的页框便被支持。PAE系统下,:0位用于偏移,2M大小的页框可用。而在位的IA-e架构中,页框大小可达到1GB或2MB,取决于是否使用PDE和PTE。
Linux对大页的支持,取决于页表项的PS标志。如果为1,意味着仅需翻译到相应层级的页表即可。了解了硬件支持,我们接着看软件层面的操作。
软件层面,Linux提供了两种大页:标准大页(Huge Page)和透明大页(Transparent Huge Page)。标准大页采用内存池管理,每个大页对应一个内存池,x系统最多支持2MB和1GB的两种。内存池管理涉及的数据结构包括最大、当前、空闲、预留和超额的大页数量,以及内存池的配置方法。
标准大页的分配流程涉及内存池预留、内存池初始化和通过mmap等方法的申请。在mmap中,标记MAP_HUGETLB用于申请大页。透明大页则解决了标准大页的局限,通过khugepaged任务动态合并连续内存,对用户代码保持透明。
透明大页的实现涉及khugepaged任务的创建、扫描和内存合并。扫描过程中,数据结构如khugepaged_scan被用于跟踪内存操作。合并时,伙伴系统被用于物理页的申请和映射更新。
总结起来,本节详细阐述了Linux大页的硬件支持和软件实现,包括标准大页的内存池管理以及透明大页的动态合并机制,展示了这两种大页技术的原理和应用细节。
Linux内核源码分析:Linux进程描述符task_ struct结构体详解
Linux内核通过一个task_struct结构体来管理进程,这个结构体包含了一个进程所需的所有信息。它定义在include/linux/sched.h文件中,包含许多字段,其中state字段表示进程的当前状态。常见的状态包括运行、阻塞、等待信号、终止等。进程状态的切换和原因可通过内核函数进行操作。PID是系统用来唯一标识正在运行的每个进程的数字标识,tgid成员表示线程组中所有线程共享的PID。进程内核栈用于保存进程在内核态执行时的临时数据和上下文信息,通常为几千字节。内核将thread_info结构与内核态线程堆栈结合在一起,占据连续的两个页框,以便于访问线程描述符和栈。获取当前运行进程的thread_info可通过esp栈指针实现。thread_info结构包含task字段,指向进程控制块(task_struct)。task_struct结构体的flags字段用于记录进程标记或状态信息,如创建、超级用户、核心转储、信号处理、退出等。而real_parent和parent成员表示进程的亲属关系,用于查找和处理进程树中的亲属关系。
如何从官网获取各个版本Linux内核的源码
访问网址 https://www.kernel.org
在页面上找到HTTP协议旁的"Location"链接,点击它或直接访问 https://www.kernel.org/pub
浏览器将展示pub/目录下的所有文件。在此页面上,找到"linux"并点击,接着点击"kernel"即可浏览到各个版本的Linux内核源码。
特别地,pub/linux/kernel目录下还包含一个名为"Historic"的子目录,这里收藏了如linux-0.和linux-0.等早期版本的源码。
Linux HugePage
在数据库服务器的性能优化过程中,我偶然遇到了Linux HugePage这一概念。它在CPU占用率高的问题排查中起到了关键作用,虽然我对它之前并不熟悉,但通过深入研究,我逐渐理解了它的核心概念和使用策略。
Linux的大页分为标准大页(Huge Pages)和透明大页(Transparent Huge Pages),两者的主要区别在于分配方式:标准大页是预先分配的,而透明大页是动态分配的。在Oracle服务器中,由于可能出现性能问题和系统重启的风险,建议禁用透明大页。在Oracle Linux 6.5中,已经移除了这一特性。
Page Table是虚拟内存与物理内存之间地址转换的关键,它保存了两者之间的对应关系。Translation Lookaside Buffer(TLB)则是CPU缓存的一部分,用于存储Page Table的部分信息。而对于大页,TLB中会包含大页的条目,而hugetlbfs则是一个内存中的文件系统,用于分配大页内存。
HugePages的优点在于它们能提高内存访问效率。通过监控系统状态,可以查看AnonHugePages(匿名大页数量,Oracle Linux 6.5已移除相关计数器)、HugePages_Total(分配的页面总数)、HugePages_Free(未使用的页面数)等指标,以确定实例所需的页面数量。设置大页大小通常涉及修改内核参数vm.nr_hugepages,并在Oracle环境中参考特定计算方法进行调整。
尽管Oracle建议禁用透明大页,但在某些情况下,可以仅预留HugePages供Oracle使用。例如,通过调整memlock参数限制Oracle的内存锁定,验证Oracle用户的memlock值,以及在G及以后版本中禁用Automatic Memory Management (AMM)以与HugePages兼容。在数据库重启后,可能需要运行hugepages_settings.sh脚本来确定最佳的大页设置。
这些知识对于优化数据库性能和理解Linux内存管理机制至关重要,如有兴趣深入学习,可以参考Dpdk/网络协议栈/vpp/OvS/DDos/NFV/虚拟化/高性能专家的免费资源,或加入相关学习群组获取更多信息。