1.8086模拟器8086tiny源码分析(8)执行mov指令(五)段寄存器拾遗
2.Lua5.4 源码剖析——性能优化与原理分析
3.鸿蒙轻内核M核源码分析:中断Hwi
4.Linux内核源码解析---万字解析从设计模式推演per-cpu实现原理
5.ThreadPoolExecutor简介&源码解析
6.CPU之超线程技术浅析
8086模拟器8086tiny源码分析(8)执行mov指令(五)段寄存器拾遗
分析模拟器tiny源码中关于mov指令与内存访问的码分处理
在分析mov指令时,我们关注到了指令可能访问内存,码分这自然引出了CPU内存地址的码分结构问题。内存地址通常由两部分组成:段寄存器和位偏移地址。码分
在我们的码分分析中,大部分关注的码分httphandle源码都是偏移地址,但事实上,码分段寄存器通常默认为DS(数据段寄存器),码分除非通过段跨越前缀修改。码分
以mov [bx],码分h为例,编译后指令序列显示为:0xc7,码分0x,0x,0x。而如果我们修改段前缀为ss,码分即mov ss:[bx],码分h,则指令序列变为:0x,码分0xc7,0x,0x,0x,这里多出了一字节。码分
那么,tiny在处理段前缀时是如何操作的呢?答案是通过宏SEGREG。如果使用了段跨越前缀,参数1会决定使用哪个段寄存器,通常默认为DS;而参数2则决定偏移寄存器1的使用。
参数3由两部分组成:一部分是偏移寄存器2,另一部分则是内存地址。最终,地址计算方式为:段寄存器* + 偏移寄存器1 + 偏移寄存器2 + 内存地址。这使得指令能够准确指向内存位置。
Lua5.4 源码剖析——性能优化与原理分析
本篇教程将引导您深入学习Lua在日常编程中如何通过优化写法来提升性能、降低内存消耗。在讲解每个优化案例时,将附上部分Lua虚拟机源代码实现,帮助您理解背后的原理。 我们将对优化的评级进行标注:0星至3星,推荐评级越高,优化效果越明显。优化分为以下类别:CPU优化、内存优化、堆栈优化等。 测试设备:个人MacBookPro,配置为4核2.2GHz i7处理器。使用Lua自带的os.clock()函数进行时间测量,以精确到毫秒级别。为了突出不同写法的性能差异,测试通常循环执行多次并累计总消耗。 下面是推荐程度从高到低的优化方法: 3星优化:全类型通用CPU优化:高频访问的对象应先赋值给local变量。示例:用循环模拟高频访问,每次访问math.random函数创建随机数。推荐程度:极力推荐。
String类型优化:使用table.concat函数拼接字符串。示例:循环拼接多个随机数到字符串。推荐程度:极力推荐。
Table类型优化:Table构造时完成数据初始化。示例:创建初始值为1,2,3的Table。推荐程度:极力推荐。
Function类型优化:使用尾调用避免堆栈溢出。flash制作游戏源码示例:递归求和函数。推荐程度:极力推荐。
Thread类型优化:复用协程以减少创建和销毁开销。示例:执行多个不同函数。推荐程度:极力推荐。
2星优化:Table类型优化:数据插入使用t[key]=value方式。示例:插入1到的数字。推荐程度:较为推荐。
1星优化:全类型通用优化:变量定义时同时赋值。示例:初始化整数变量。推荐程度:一般推荐。
Nil类型优化:相邻赋值nil。示例:定义6个变量,其中3个为nil。推荐程度:一般推荐。
Function类型优化:不返回多余的返回值。示例:外部请求第一个返回值。推荐程度:一般推荐。
0星优化:全类型通用优化:for循环终止条件无需提前计算缓存。示例:复杂函数计算循环终止条件。推荐程度:无效优化。
Nil类型优化:初始化时显示赋值和隐式赋值效果相同。示例:定义一个nil变量。推荐程度:无效优化。
总结:本文从源码层面深入分析了Lua优化策略。请根据推荐评级在日常开发中灵活应用。感谢阅读!鸿蒙轻内核M核源码分析:中断Hwi
在鸿蒙轻内核源码分析系列中,本文将深入探讨中断模块,旨在帮助读者理解中断相关概念、鸿蒙轻内核中断模块的源代码实现。本文所涉及源码基于OpenHarmony LiteOS-M内核,读者可通过开源站点 gitee.com/openharmony/k... 获取。中断概念介绍
中断机制允许CPU在特定事件发生时暂停当前执行的任务,转而处理该事件。这些事件通常由外部设备触发,通过中断信号通知CPU。中断涉及硬件设备、中断控制器和CPU三部分:设备产生中断信号;中断控制器接收信号并发出中断请求给CPU;CPU响应中断,执行中断处理程序。中断相关的硬件介绍
硬件层面,中断源分为设备、中断控制器和CPU。设备产生中断信号;中断控制器接收并转发这些信号至CPU;CPU在接收到中断请求后,暂停当前任务,转而执行中断处理程序。中断相关的概念
每个中断信号都附带中断号,用于识别中断源。中断优先级根据事件的重要性和紧迫性进行划分。当设备触发中断后,CPU中断当前任务,执行中断处理程序。虚拟网站隐藏源码中断处理程序由设备特定,且通常以中断向量表中的地址作为入口点。中断向量表按中断号排序,存储中断处理程序的地址。鸿蒙轻内核中断源代码
中断相关的声明和定义
在文件 kernel\arch\arm\cortex-m7\gcc\los_interrupt.c 中定义了结构体、全局变量和内联函数。关键变量 g_intCount 记录当前正在处理的中断数量,内联函数 HalIsIntActive() 用于检查是否正在处理中断。中断向量表在中断初始化过程中设置,用于映射中断号到相应的中断处理程序。中断初始化 HalHwiInit()
系统启动时,在 kernel\src\los_init.c 中初始化中断。HalHwiInit() 函数在 kernel\arch\arm\cortex-m7\gcc\los_interrupt.c 中实现,负责设置中断向量表和优先级组,配置中断源,如系统中断和定时器中断。创建中断 HalHwiCreate()
开发者可通过 HalHwiCreate() 函数注册中断处理程序,传入中断号、优先级和中断模式。函数内部验证参数,设置中断处理程序,最终通过调用 CMSIS 函数完成中断创建。删除中断 HalHwiDelete()
中断删除操作通过 HalHwiDelete() 实现,接收中断号作为参数,调用 CMSIS 函数失能中断,设置默认中断处理程序,完成中断删除。中断处理执行入口程序
默认的中断处理程序 HalHwiDefaultHandler() 仅用于打印中断号后进行死循环。HalInterrupt() 是中断处理执行入口程序的核心,它包含中断数量计数、中断号获取、中断前后的操作以及调用中断处理程序的逻辑。开关中断
开关中断用于控制CPU是否响应外部中断。通过宏 LOS_IntLock() 关闭中断, LOS_IntRestore() 恢复中断状态, LOS_IntUnLock() 使能中断。这组宏对应汇编函数,使用寄存器 PRIMASK 控制中断状态。小结
本文详细解析了鸿蒙轻内核中断模块的源代码,涵盖了中断概念、初始化、创建、删除以及开关操作。后续文章将带来更多深入技术分享。欢迎在 gitee.com/openharmony/k... 分享学习心得、提出问题或建议。关注、点赞、Star 和 Fork 到个人账户,便于获取更多资源。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段中。
在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"的wx抓娃娃源码偏移量。加上numa_node相对于原始内存副本的偏移量,最终通过解引用获得真正内存地址内的值。
对于其他架构,实现方式相似,通过获取自己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。为每个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中,我们关注重点区域,完成map数组的slab分配。
至此,我们探讨了Linux内核中per-cpu实现的原理,从设计到源码分析,全面展现了这一关键机制在现代服务器架构中的作用。
ThreadPoolExecutor简介&源码解析
线程池是通过池化管理线程的高效工具,尤其在多核CPU时代,利用线程池进行并行处理任务有助于提升服务器性能。ThreadPoolExecutor是线程池的具体实现,它负责线程管理和任务管理,以及处理任务拒绝策略。这个类提供了多种功能,如通过Executors工厂方法配置,执行Runnable和Callable任务,维护任务队列,统计任务完成情况等。
创建线程池需要考虑关键参数,如核心线程数(任务开始执行时立即创建),最大线程数(任务过多时限制新线程生成),线程存活时间,任务队列大小,线程工厂以及拒绝策略。JDK提供了四种拒绝策略,如默认的AbortPolicy,当资源饱和时抛出异常。此外,线程池还提供了beforeExecute和afterExecute钩子函数,用于在任务执行前后执行自定义操作。
当任务提交到线程池时,会经历一系列处理流程,包括任务的执行和线程池状态的管理。例如,如果任务队列和线程池满,会根据拒绝策略处理新任务。使用线程池时,需注意线程池容量与状态的计算,以及线程池工作线程的动态调整。
示例中,自定义线程池并重写钩子函数,创建任务后向线程池提交,可以看到线程池如何根据配置动态调整资源。但要注意,如果任务过多且无法处理,可能会抛出异常。源码分析中,submit方法实际上是调用execute,而execute内部包含Worker类和runWorker方法的逻辑,包括任务的获取和执行。
线程池的容量上限并非Integer.MAX_VALUE,而是由ctl变量的低位决定。 Doug Lea的工具函数简化了ctl的操作,使得计算线程池状态和工作线程数更加便捷。通过深入了解ThreadPoolExecutor,开发者可以更有效地利用线程池提高应用性能。
CPU之超线程技术浅析
最近在研究openjdk源码时,遇到了“超线程”这个词,引起了我的兴趣。其实,我们的机房机器都支持这种技术。
例如,我们服务使用的是一款单CPU,4物理核,8逻辑核(超线程,HTT)的服务器,主频3.4G。而新机型是双CPU,每个CPU物理核,逻辑核(HTT),共核,但主频只有2.5G。这个服务是时延敏感的服务,但低主频的新版CPU在更小制程更新架构的情况下,性能表现可能超过旧版高主频CPU。
多核CPU主频更低的原因主要是功耗考虑。个物理核密集布在一块板上,高主频会带来高功耗,散热问题很大。因此,目前市面上的这种几十核CPU,主频基本都在2.xG左右。
为了充分利用多核CPU性能,程序的线程数需要大大提高,但这会不会带来锁的争用从而导致性能下降?现在的服务基本是一个无锁的计算密集型程序。
服务请求量变大后,新机器上的时延直接飙到原来的两倍还多。请教了运维同学后,尝试关闭超线程,发现时延只比原来的8核机高不到%,且随负载增加没有明显变化,符合上游服务的时延要求。
为什么关闭超线程有效呢?
首先,我们需要了解什么是超线程。超线程技术基于这样一个现实:大多数程序运行时,CPU资源并没有得到充分利用。例如,CPU缓存未命中、分支预测错误或等待数据时,CPU中的计算资源其实是闲置的。超线程技术,通过硬件指令,将这些闲置的CPU资源,调度给其他的指令,从而整体上提高CPU的资源利用率。
关闭超线程后,为什么时延性能提升了呢?可能有以下几个方面的原因。另外,超线程程序会带来耗电量的大幅提升。这在服务器程序来说,可能带来功耗的提升导致散热问题从而降频,而对于目前智能手机来说,电池续航也是一个严重的问题。
超线程技术,目前基本上是服务器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.。
总结:当系统运行队列长度、调度延迟和平均负载达到一定值时,需要关注系统性能并进行优化。运行队列长度、调度延迟和平均负载是衡量系统性能的重要指标,通过监控和分析这些指标,可以及时发现和解决问题,提高系统的稳定性和响应速度。
Linux /proc/cpuinfo代码的实现
为了获取系统中CPU的详细配置信息,Linux提供了一个名为/proc/cpuinfo的文件。这个文件可以被系统命令cat轻松查看。
对于ARM架构的芯片,其代码实现主要在arch/arm/kernel/head.S中,通过调用函数__lookup_processor_type来检查系统是否支持特定CPU,并获取相关procinfo信息。procinfo是一个proc_info_list类型的结构体,用于抽象表示每种处理器。
在Linux内核源代码中,/proc/cpuinfo的实现通常位于fs/proc/cpuinfo.c或类似文件中。这里定义了一个proc_dir_entry结构体实例来代表/proc/cpuinfo文件。不同类型的CPU芯片实现相同的接口但提供不同的内容,show_cpuinfo函数负责生成CPU信息,这些信息以序列化的方式生成。
在ARM架构中,for_each_online_cpu宏用于遍历系统中所有在线的CPU,并在每次迭代中执行特定操作,对每个CPU执行特定的操作。这一宏在内核模块或内核代码中广泛使用,特别是在需要操作或收集每个CPU信息时。
在模块初始化函数fs_initcall(proc_cpuinfo_init)中,通过proc_create函数将/proc/cpuinfo条目注册到/proc文件系统中,并关联定义的file_operations结构体。内核编译与加载后,/proc/cpuinfo条目就会被创建,准备好在用户空间程序请求时提供CPU信息。
对于x架构,虽然函数名称可能不同,但实现原理类似,提供CPU信息的获取与展示。快捷查询命令如cat /proc/cpuinfo可快速查看系统CPU的详细配置。
在离线混部-Koordinator Cpu Burst 特性 源码调研
在离线混部场景下,Koordinator引入了Cpu Burst特性来优化CPU资源管理。这个特性源自Linux内核的CPU Burst技术,旨在处理突发的CPU使用需求,减少CPU限流带来的影响。cgroups的参数如cpu.share、cpu.cfs_quota_us和cpu.cfs_burst,分别控制了CPU使用率、配额和突发缓冲效果。在Kubernetes中,资源请求(requests.cpu)和限制(limits.cpu)通过这些参数来实现动态调整,以保证容器间公平的CPU分配。
对于资源调度,Kubernetes的Bandwidth Controller通过时间片限制进程的CPU消耗,针对延迟敏感业务,如抖音视频服务,通过设置合理的CPU limits避免服务质量下降,同时也考虑资源的高效利用。然而,常规的限流策略可能导致容器部署密度降低,因为时间片间隔可能不足以应对突发的CPU需求。CPU Burst技术正是为了解决这个问题,通过收集未使用的CPU资源,允许在突发时使用,从而提高CPU利用率并减少throttled_time。
在Koordinator的配置中,通过configMap可以调整CPU Burst的百分比,以及在负载过高时的调整策略。例如,当CPU利用率低于阈值时,允许动态扩展cfs_quota,以应对突发的CPU使用。源码中,会根据节点负载状态和Pod的QoS策略来调整每个容器的CPU Burst和cfs_quota。
总的来说,Cpu Burst特性适用于资源利用率不高且短作业较多的场景,能有效提升核心业务的CPU资源使用效率,同时对相邻容器的影响较小。在某些情况下,结合cpuset的核绑定和NUMA感知调度可以进一步减少CPU竞争。理解并灵活运用这些技术,有助于优化云计算环境中的资源分配和性能管理。