1.Linux内核源码解析---万字解析从设计模式推演per-cpu实现原理
2.unmatched(riscv64)上编译,自己做c自己做安装和移植SPEC CPU 2006
3.一篇讲解CPU性能指标提取及源码分析
4.Vivado实战—单周期CPU指令分析
Linux内核源码解析---万字解析从设计模式推演per-cpu实现原理
引子
在如今的大型服务器中,NUMA架构扮演着关键角色。动手动手它允许系统拥有多个物理CPU,源码源码不同NUMA节点之间通过QPI通信。自己做c自己做虽然硬件连接细节在此不作深入讨论,动手动手但需明白每个CPU优先访问本节点内存,源码源码IIR源码当本地内存不足时,自己做c自己做可向其他节点申请。动手动手从传统的源码源码SMP架构转向NUMA架构,主要是自己做c自己做为了解决随着CPU数量增多而带来的总线压力问题。
分配物理内存时,动手动手numa_node_id() 方法用于查询当前CPU所在的源码源码NUMA节点。频繁的自己做c自己做内存申请操作促使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的ommo源码定义,最终揭示numa_node是一个共享全局变量,类型为int,存储在.data..percpu段中。
在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相对于原始内存副本的ugp源码偏移量,最终通过解引用获得真正内存地址内的值。
对于其他架构,实现方式相似,通过获取自己CPU的偏移量,最终通过相对偏移得到pcp变量的地址。
放入数据
讨论Linux内核启动过程时,我们不得不关注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。resilience源码为每个CPU创建副本,其中存储所有per-cpu变量。静态数据在编译时放入原始数据段,动态数据在运行时生成。
最后,我们回到setup_per_cpu_areas方法的分析。在setup_percpu.c中,我们详细探讨了关键方法pcpu_embed_first_chunk。此方法管理group、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中,我们关注重点区域,caffelstm源码完成map数组的slab分配。
至此,我们探讨了Linux内核中per-cpu实现的原理,从设计到源码分析,全面展现了这一关键机制在现代服务器架构中的作用。
unmatched(riscv)上编译,安装和移植SPEC CPU
为了在unmatched系统上编译、安装和移植SPEC CPU ,首先需要检查系统信息如下: Linux ubuntu 5..0--generic #-Ubuntu SMP Tue Sep :: UTC riscv riscv riscv GNU/Linux 然后,需要安装编译工具:gcc, g++, gfortran。检查安装是否正确,复制SPEC CPU 源码。 因为SPEC CPU 源码中自带的toolset不支持RISC-V,需自行编译。安装并检查gcc、g++、gfortran后,将spec cpu 源码复制出来,替换旧的config.guess, config.sub文件,使用最新版本的文件。 接下来,在toolset源码路径下执行./buildtools编译toolset。在编译过程中,可能会遇到错误,需解决如下问题:出现__alloca'和__stat'未定义错误:注释掉glob/glob.c文件中第和第行。
出现重复定义错误:执行export CFLAGS="$CFLAGS -fcommon"。
'gets' undeclared错误:注释掉stdio.in.h中的相应行。
pow、floor、fmod、sin等函数未定义:执行export PERLFLAGS="-A libs=-lm -A libs=-ldl -A libs=-lc -A ldflags=-lm -A cflags=-lm -A ccflags=-lm -Dlibpth=/usr/lib/riscv-linux-gnu -A ccflags=-fwrapv"。
error building Perl错误:修改Configure文件中的相关行。
error running TimeDate-1.测试套件:修改getdate.t文件中的第行。
解决上述错误后,再次编译toolset,若部分Perl测试项未通过,输入y确认。编译成功后,验证工具集构建是否正确。在指定目录下创建文件夹并打包toolset,生成tar文件。 之后,在同一目录下运行install.sh进行安装。遇到错误时,查看runspec-test.linux-riscv.out文件,并在perl-5..3/Configure文件中添加代码。重新编译并打包工具集后,再次安装以解决校验和检查错误。 最后,如果希望直接在其他unmatched上移植已编译并打包的工具集,按照上述操作执行即可。这样,无需重复编译过程,便可以直接进行SPEC CPU 的测试。一篇讲解CPU性能指标提取及源码分析
这篇报告主要根据CPU性能指标——运行队列长度、调度延迟和平均负载,对系统的性能影响进行简单分析。
CPU调度程序运行队列中存放的是那些已经准备好运行、正等待可用CPU的轻量级进程。如果准备运行的轻量级进程数超过系统所能处理的上限,运行队列就会很长,运行队列长表明系统负载可能已经饱和。
代码源于参考资料1中map.c用于获取运行队列长度的部分代码。
在系统压力测试前后,使用压力测试工具stress-ng,可以看到运行队列长度的明显变化,从3左右变化到了左右。
压力测试工具stress-ng可以用来进行压力测试,观察系统在压力下的表现,例如运行队列长度、调度延迟、平均负载等性能指标。
在系统运行队列长度超过虚拟处理器个数的1倍时,需要关注系统性能。当运行队列长度达到虚拟处理器个数的3~4倍或更高时,系统的响应就会非常迟缓。
解决CPU调用程序运行队列过长的方法主要有两个方面:优化调度算法和增加系统资源。
所谓调度延迟,是指一个任务具备运行的条件(进入 CPU 的 runqueue),到真正执行(获得 CPU 的执行权)的这段时间。通常使用runqlat工具进行测量。
在正常情况下使用runqlat工具,可以查看调度延迟分布情况。压力测试后,调度延迟从最大延迟微秒变化到了微秒,可以明显的看到调度延迟的变化。
平均负载是对CPU负载的评估,其值越高,说明其任务队列越长,处于等待执行的任务越多。在系统压力测试前后,通过查看top命令可以看到1分钟、5分钟、分钟的load average分别从0.、1.、1.变化到了4.、3.、1.。
总结:当系统运行队列长度、调度延迟和平均负载达到一定值时,需要关注系统性能并进行优化。运行队列长度、调度延迟和平均负载是衡量系统性能的重要指标,通过监控和分析这些指标,可以及时发现和解决问题,提高系统的稳定性和响应速度。
Vivado实战—单周期CPU指令分析
引言
探索《计算机组成原理》的学习之路,仿佛在零和一之间跋涉,看似简单的二进制码却构筑起计算机世界的复杂迷宫。从源码、反码、补码的转换中,感受到计算机世界的深奥与奥秘。随着课程的深入,计算机从一个“没有思想的空壳”变为承载智慧的“电子伙伴”,而程序员则成为赋予它们“灵魂”的“程序猿”。CPU(中央处理器)作为计算机的核心,其功能与实现是理解计算机原理的关键。
单周期CPU功能指令分析
深入解析单周期CPU的条指令,以实现对指令执行过程的准确理解和验证。通过上机实验,逐一检验每个指令的功能实现与理论结果的相符性,展现计算机硬件处理指令的精确逻辑。
指令解析与实现过程
以Addi指令为例,分析指令的执行过程,即rt←rs + (sign-extend)immediate,通过查找rs的源操作数地址,immediate的值以及ALU的计算结果,验证指令实现的正确性。类似地,对Ori、Add、Sub、and、or、sll、bne、slti、beq、sw、lw、j和halt指令进行逐一解析与实现过程的验证,确保指令执行符合预期。
总结与反思
通过单周期CPU的指令分析,不仅深入了解了计算机硬件的指令执行机制,还体会到了分层和模块化设计的重要性。VerilogHDL语言的自顶向下、分而治之的思想,为单周期CPU的设计与实现提供了强大的工具。尽管未能在硬件上实现,但理论与实践的结合,加深了对计算机原理的理解。在模块调用时,端口绑定的小技巧,为代码调试和优化提供了便利。借助维基百科、谷歌学术等资源,进一步扩展了知识边界,为后续学习打下了坚实的基础。