1.qemu调试kernel启动(从第一行汇编开始)
2.Linux 内核启动及文件系统加载过程
3.linux内核$(kallsyms.o)详解续篇 --- 内核符号表的源码生成和查找过程
4.「技术干货」ARM64内核源码解读:mmu-gather操作
5.uclinux内存管理
qemu调试kernel启动(从第一行汇编开始)
在深入理解Linux启动流程时,关注的源码焦点通常在于start_kernel之后的内核初始化,但在正式调试之前,源码先要知道从第一行汇编代码开始的源码调试方法。关键步骤在于正确加载symbols到物理或虚拟地址,源码这取决于MMU的源码kafka connect 源码状态。
在使用qemu进行调试时,源码启动时添加-S选项会显示物理地址,源码如0x,源码但需注意不同qemu版本可能有所不同,源码以Ubuntu .自带的源码6.2.0版本为例,kernel的源码物理起始地址是0x。而在vmlinux中,源码_text段的源码虚拟地址为0xffff。
为了将物理地址和vmlinux中的源码地址对齐,需要查看qemu源码中的hw/arm/boot.c部分,确认哪些段需要映射。例如,通过add-symbol-file命令,指定如下地址映射关系:.head.text到0x,.text到0x等。设置断点在_text处,如b _text,即可开始单步调试。
总结来说,不论是哪种调试器,首要任务是将elf文件的执行地址与目标执行地址(物理或虚拟)对齐,这是react源码不用ts调试入口的关键。理解并掌握这一原则,能让你更有效地进行内核调试工作。
Linux 内核启动及文件系统加载过程
Linux内核启动及文件系统加载过程
在u-boot引导Linux内核开始执行bootcmd命令后,Linux内核启动过程便开始。普通Linux内核启动通常分为三个阶段。本文将根据项目中使用的linux-2.6.源码,详细描述内核启动的全过程。
第一阶段为内核自解压过程。在启动过程中,可以看到内核自解压界面。压缩和解压缩代码位于kernel/arch/arm/boot/compressed目录下。编译后产生head.o、misc.o、piggy.gzip.o、vmlinux、decompress.o等文件。BootLoader将系统引导至内核内存后,调用do_bootm_linux(),若内核未被压缩,直接启动;如被压缩,则进行解压。压缩内核头部包含解压程序,入口文件位于arch/arm/boot/compressed/head.S。
decompress_kernel()函数在misc.c文件中实现,调用proc_decomp_setup()、arch_decomp_setup()进行设置。解压缩过程开始后,小程序小店源码打印“Uncompressing Linux...”信息,调用gunzip()解压并放置指定位置。内核解压完成后,返回head.S的行,启动内核。
第二阶段主要工作包括设置ARM处理器工作模式、启用MMU、设置一级页表等。汇编代码通过查找处理器内核类型和机器码类型调用相应的初始化函数,建立页表,跳转至start_kernel()函数开始内核初始化。处理器类型检测在汇编子函数__lookup_processor_type中完成,机器码类型检测在__lookup_machine_type中。建立页表后,跳转至C程序入口start_kernel()。
第三阶段由start_kernel函数开始,这是所有Linux平台进入系统内核初始化后的入口。它完成与硬件平台相关的初始化工作,调用init进程并等待执行,完成Linux内核启动。
启动init服务并挂载根文件系统
在启动init服务前,需要挂载根文件系统。根文件系统至少包含五大目录。以只读方式挂载,防止内核不稳定时破坏数据或延长开机时间。
挂载根文件系统后,执行init服务。axure仓库管理源码Linux内核依据/etc/inittab文件中设置进行初始化系统环境。init启动后,表示Linux内核成功启动。
在启动init服务时,init会读取/etc/inittab文件,执行rc.sysinit、rc、rc.local等脚本进行系统初始化。rc.sysinit主要功能包括启动udev、设置内核参数、系统时间、启用交换内存、检查并挂载文件系统、初始化硬件设备、管理串行端口、清除过期锁定文件、建立用户接口等。
整个Linux内核启动过程至此完成。
linux内核$(kallsyms.o)详解续篇 --- 内核符号表的生成和查找过程
在内核中,维护着一张符号表,记录着内核中的所有符号,包括函数与全局变量的地址与名称。这张表嵌入在内核镜像中,供内核运行时随时查找符号名。通过调用__print_symbol,内核代码能打印出符号名。 接下来,Skyworking源码目录结构我们将详细解析内核符号表的生成与查找过程。系统映像文件与/proc/kallsyms的区别与联系
系统映像文件(System.map)在编译内核时生成,记录了内核中的所有符号及其在内存中的虚拟地址。文件由scripts/mksysmap脚本生成,依赖于nm命令。系统映像文件的每条记录包括地址、符号类型与符号名。符号类型包括函数、全局变量等。 而/proc/kallsyms文件是在内核启动后自动生成,位于/proc目录下,其代码实现于kernel/kallsyms.c。区别在于它包含了内核模块的符号列表,并且允许用户态访问,非内核常规操作。通常,我们只需关注_stext ~ _etext 和 _sinittext ~ _einittext之间的符号。内核符号表的生成与查找
内核在运行过程中可能需要查找地址对应的函数名,如Oops或调试信息输出。但内核并未依赖System.map或/proc/kallsyms文件,而是通过vmlinux中的符号表(.tmp_vmlinux2.o)实现快速查找。内核符号表结构
内嵌符号表通过scripts/kallsyms工具生成,源码位于kallsyms.c。该表包含6个全局变量:kallsyms_addresses、kallsyms_num_syms、kallsyms_names、kallsyms_token_table、kallsyms_token_index与kallsyms_markers。其中,kallsyms_addresses记录所有符号地址,kallsyms_num_syms统计符号数量,kallsyms_names存放符号名,kallsyms_token_table与kallsyms_token_index用于压缩存储高频率字符串。压缩算法与优化
内核使用压缩算法减少存储开销,将高频率字符串表示为token,并通过kallsyms_token_table与kallsyms_token_index实现压缩与解压。kallsyms_markers将符号每个分组,加速查找过程。查找过程实例与源码分析
举例说明查找地址0xc对应的符号名。首先在System.map中定位到该地址位于__create_page_tables与__enable_mmu之间。在.tmp_kallsyms2.S文件中,利用二分查找定位符号地址,然后通过kallsyms_names与kallsyms_markers加速查找过程。最后解析压缩的符号名,得到结果为__enable_mmu。内核模块符号查找
内核模块在启动时动态加载,其符号表存储在struct module结构中,所有已加载模块的struct module结构构成全局链表。查找内核模块中的符号时,调用kallsyms_lookup()函数,模块符号查找由get_ksymbol()函数完成。「技术干货」ARM内核源码解读:mmu-gather操作
本文深入解析了Linux内核虚拟内存管理中的mmu_gather操作,该操作确保了tlb刷新与物理页面释放的有序执行,并能将多个页面聚集起来统一释放。
在进程退出、执行munmap或执行execv等情况下,内核会解除虚拟内存区域的页表映射、刷新tlb并释放物理页面。这一过程遵循特定顺序:解除页表映射、刷新tlb、释放物理页面。在刷新tlb之前,不能先释放物理页面,否则可能导致不正确的结果,而mmu_gather(mmu积聚)的作用就是确保这一顺序,并将需要释放的物理页面聚集起来统一释放。
mmu_gather操作涉及三个关键数据结构:mmu_gather、mmu_table_batch、以及mmu_gather_batch。mmu_gather用于表示一次mmu积聚操作,包含了操作进程、积聚页目录物理页面、起始和结束虚拟地址、是否操作整个用户地址空间、是否为可执行或hugetlb的vma等信息。mmu_table_batch用于积聚进程使用的页目录物理页面,而mmu_gather_batch则表示物理页的积聚批次,用于积聚映射到用户空间的物理页。
整体调用由tlb_gather_mmu、unmap_vmas、free_pgtables和tlb_finish_mmu等函数组成。其中,tlb_gather_mmu初始化mmu_gather结构,unmap_vmas解除页表映射,free_pgtables释放页表,tlb_finish_mmu进行tlb刷新与物理页面释放。
在unmap_vmas函数中,解除相关进程虚拟内存区域的页表映射,并将物理页面放入积聚结构中进行统一释放。通过遍历进程页表并解除映射关系,将物理页面加入到积聚结构中。
unmap_vmas函数的关键之处在于遍历页表,将映射关系解除,并将物理页面加入积聚结构。在释放页表过程中,每当一个页表积聚结构填满,就会释放该结构中的页面。最后,通过tlb_finish_mmu完成tlb刷新与物理页面释放的最终步骤。
mmu_gather操作适用于进程退出、execv执行和munmap调用等场景,确保了内存资源的有序释放,优化了内核的内存管理效率。
uclinux内存管理
在uClinux和标准Linux中,内存管理是显著的区别点,尤其在没有MMU的处理器环境下,这导致了与标准Linux不同的问题。标准Linux利用虚拟存储器技术,提供大量虚拟内存空间,通过分页机制支持进程管理。它依赖于存储器管理机制和硬盘,利用局部性原理,按需加载和交换内存,保护不同任务间的地址空间,支持大型程序运行、程序加载优化、内存共享和保护等功能。 标准Linux是为有MMU的处理器设计,虚拟地址由MMU转换为物理地址,确保进程的隔离。然而,对于uClinux,设计目标是针对无MMU处理器,采用实存管理,不支持虚拟地址映射,而是直接访问物理地址。这要求在程序加载时预先分配连续地址空间,且内存管理和保护机制简单,可能导致开发人员需要更多地参与内存管理,如在启动时指定内存大小,避免内存不足引发的问题。 在多进程处理上,uClinux使用vfork代替标准的fork,因为缺少MMU,程序执行时需要重新定位和加载。这与内存管理紧密相关,因为每个新进程的加载都会占用实际内存,且不能依赖磁盘交换。这种内存管理方式对开发者提出了更高的要求,特别是对内存分配和程序设计的精确性。 总的来说,尽管uClinux的内存管理在功能上与标准Linux有较大差距,但其扁平的内存管理模式和对成本敏感的嵌入式设备环境更为适用,开发者需要更多地考虑硬件限制和内存管理策略。对于简单的嵌入式应用,这种直接的内存访问和简单的多进程管理可能更为合适。扩展资料
uclinux表示micro-control linux.即“微控制器领域中的Linux系统”,是Lineo公司的主打产品,同时也是开放源码的嵌入式Linux的典范之作。uCLinux主要是针对目标处理器没有存储管理单元MMU(Memory Management Unit)的嵌入式系统而设计的。它已经被成功地移植到了很多平台上。由于没有MMU,其多任务的实现需要一定技巧。