kernel启动流程-start_kernel的执行_3.build_all_zonelists

本文详细介绍了Linux内核5.10版本中,内存管理的关键过程`build_all_zonelists`。该过程在系统启动时,依据内存成本从低到高建立节点间的内存分配顺序,并初始化每个节点的zone列表。`pg_data_t`结构体描述了内存布局,`build_all_zonelists_init`和`__build_all_zonelists`函数负责构建zone列表,`nr_free_zone_pages`统计可用页面,`page_group_by_mobility_disabled`根据内存大小决定是否启用流动分组机制。整个过程确保了内存的有效管理和分配。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 前言

本专题文章承接之前《kernel启动流程_head.S的执行》专题文章,我们知道在head.S执行过程中保存了bootloader传递的启动参数、启动模式以及FDT地址等,创建了内核空间的页表,最后为init进程初始化好了堆栈,并跳转到start_kernel执行。
本文重点介绍start_kernel的build_all_zonelists的主要流程.

kernel版本:5.10
平台:arm64

2. pg_data_t

typedef struct pglist_data { 
	struct zone node_zones[MAX_NR_ZONES];
	struct zonelist node_zonelists[MAX_ZONELISTS];
	int nr_zones;
} pg_data_t; 

先来重点看下pg_data_t结构体,有一段注释

On NUMA machines, each NUMA node would have a pg_data_t to describe it’s memory layout. On UMA machines there is a single pglist_data which describes the whole memory. Memory statistics and page replacement data structures are maintained on a per-zone basis.

注释中说每个NUMA节点都会有一个pg_data_t结构体变量来描述它的内存布局,在UMA机器只会有一个pg_data_t结构体变量来描述所有的内存。每个zone都会自己维护内存统计和页面替换
主要的成员说明如下:

成员说明
node_zones本地node的zone数组,不包含其它node
node_zonelistsnode_zonelists包含对所有节点中所有zone的引用, 它记录了zone的内存分配顺序
nr_zones当前node的zone数量

3.build_all_zonelists

build_all_zonelists主要的工作就是在当前处理的结点和系统中其它节点的内存域之间建立一种等级次序,之后将根据这种次序来分配内存。
内存定义了两条内存域的排序规则:

  1. 按照从低廉到昂贵的顺序创建单个节点内存域的排序规则:
    high zone->normal zone->DMA zone
  2. 对于N个节点中的m节点来说,为保证不过度使用任一节点, 选择备用节点的顺序为:
    m, m+1, m+2, …, N-1, 0, 1, …, m-1

注:参考自<<深入Linux内核架构>> 内存管理,关于节点的顺序未必适用于5.10,5.10是通过find_next_best_node来寻找下一个最佳节点

|- -build_all_zonelists_init

if (system_state == SYSTEM_BOOTING) {
        build_all_zonelists_init();
} else {
        __build_all_zonelists(pgdat);
        /* cpuset refresh routine should be here */
}

build_all_zonelists首先会判断system_state的状态,system_state用于记录系统的状态,状态机有如下几种,它们的顺序是不能任意改变的,代码中会有很多地方的判断依赖状态机的顺序

extern enum system_states {
        SYSTEM_BOOTING, 
        SYSTEM_SCHEDULING,                                                                                                                                                           
        SYSTEM_RUNNING,                                                                                                                                                              
        SYSTEM_HALT,                                                                                                                                                                 
        SYSTEM_POWER_OFF,                                                                                                                                                            
        SYSTEM_RESTART,                                                                                                                                                              
        SYSTEM_SUSPEND,                                                                                                                                                              
} system_state;

如果为SYSTEM_BOOTING,则执行build_all_zonelists_init(),否则执行__build_all_zonelists(pgdat), 此处由于刚启动,
system_state == SYSTEM_BOOTING,因此会执行build_all_zonelists_init

build_all_zonelists_init
    |--__build_all_zonelists(NULL)
    |      |--for_each_online_node(nid)
    |             pg_data_t *pgdat = NODE_DATA(nid)
    |             build_zonelists(pgdat)
    \--for_each_possible_cpu(cpu)
            setup_pageset(&per_cpu(boot_pageset, cpu), 0)

build_all_zonelists_init主要是设置各个内存管理节点node里面各自的内存管理区zone的内存分配次序.

|- - -__build_all_zonelists

__build_all_zonelists(NULL)
    \--for_each_online_node(nid)
          build_zonelists(pgdat)
              |--while((node=find_next_best_node(local_node,&used_mask))>= 0) 
              |       node_load[node] = load
              |       node_order[nr_nodes++] = node
              |--build_zonelists_in_node_order(pgdat, node_order, nr_nodes)
              \--build_thisnode_zonelists(pgdat)

内存节点pg_data_t 成员node_zonelists用来记录内存分配的zone顺序列表,它主要包含两类zone:一类是本node的zone;一类是当本node没有zone可以分配内存时,将从其它node的zone分配,称为后备zone。
__build_all_zonelists将为所有的内存node初始化node_zonelists成员。其中build_zonelists_in_node_order将初始化后备zonelist,build_thisnode_zonelists将初始化本地node的node_zonelists

|- - -setup_pageset

setup_pageset(&per_cpu(boot_pageset, cpu), 0)
    |--pageset_init(p)
     \--pageset_set_batch(p, batch)

setup_pageset()函数入参p是一个struct per_cpu_pageset结构体的指针,per_cpu_pageset结构是内核的各个zone用于每CPU的页面高速缓存管理结构。该高速缓存包含一些预先分配的页面,以用于满足本地CPU发出的单一内存请求。
而struct per_cpu_pages定义的pcp是该管理结构的成员,用于具体页面管理。

  1. pageset_init():主要是将struct per_cpu_pages结构体进行初始化,主要是初始化INIT_LIST_HEAD(&boot_pageset->pcp->lists[migratetype]);

  2. pageset_set_batch()则是对其进行设置

|- -nr_free_zone_pages

/* Get the number of free pages beyond high watermark in all zones. */
vm_total_pages = nr_free_zone_pages(gfp_zone(GFP_HIGHUSER_MOVABLE));

每个zone都有水位,可以看到nr_free_zone_pages()遍历所有内存管理区并将各管理区超越high water的内存空间求和,其实质是用于统计所有的管理区可以用于分配的内存页面数

|- -page_group_by_mobility_disabled

if (vm_total_pages < (pageblock_nr_pages * MIGRATE_TYPES))
	page_group_by_mobility_disabled = 1;
else
	page_group_by_mobility_disabled = 0;

判断当前系统中的内存页框数目,以决定是否启用流动分组机制(Mobility Grouping),该机制可以在分配大内存块时减少内存碎片。通常只有内存足够大时才会启用该功能,否则将会提升消耗降低性能。其中pageblock_nr_pages表示伙伴系统中的最高阶页块所能包含的页面数。

4.总结

可以看出build_all_zonelists_init主要是设置各个内存管理节点node里面各自的内存管理区zone的内存分配次序;也会统计所有的管理区可以用于分配的内存页面数

参考文档

  1. Documentations/vm/memory-model.rst
  2. https://zhuanlan.zhihu.com/p/220068494
  3. 【Linux内存源码分析】伙伴管理算法(1)
[ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x410fd034] [ 0.000000] Linux version 5.15.36-xilinx-v2022.2 (oe-user@oe-host) (aarch64-xilinx-linux-gcc (GCC) 11.2.0, GNU ld (GNU Binutils) 2.37.20210721) #1 SMP Mon Oct 3 07:50:07 UTC 2022 [ 0.000000] Machine model: xlnx,zynqmp [ 0.000000] earlycon: cdns0 at MMIO 0x00000000ff000000 (options &#39;115200n8&#39;) [ 0.000000] printk: bootconsole [cdns0] enabled [ 0.000000] efi: UEFI not found. [ 0.000000] Zone ranges: [ 0.000000] DMA32 [mem 0x0000000000000000-0x00000000ffffffff] [ 0.000000] Normal [mem 0x0000000100000000-0x000000097fffffff] [ 0.000000] Movable zone start for each node [ 0.000000] Early memory node ranges [ 0.000000] node 0: [mem 0x0000000000000000-0x000000007fefffff] [ 0.000000] node 0: [mem 0x0000000800000000-0x000000097fffffff] [ 0.000000] Initmem setup node 0 [mem 0x0000000000000000-0x000000097fffffff] [ 0.000000] On node 0, zone Normal: 256 pages in unavailable ranges [ 0.000000] cma: Reserved 256 MiB at 0x000000006b800000 [ 0.000000] psci: probing for conduit method from DT. [ 0.000000] psci: PSCIv1.1 detected in firmware. [ 0.000000] psci: Using standard PSCI v0.2 function IDs [ 0.000000] psci: MIGRATE_INFO_TYPE not supported. [ 0.000000] psci: SMC Calling Convention v1.2 [ 0.000000] percpu: Embedded 18 pages/cpu s34776 r8192 d30760 u73728 [ 0.000000] Detected VIPT I-cache on CPU0 [ 0.000000] CPU features: detected: ARM erratum 845719 [ 0.000000] Built 1 zonelists, mobility grouping on. Total pages: 2064132 [ 0.000000] Kernel command line: earlycon console=ttyPS0,115200 clk_ignore_unused root=/dev/mmcblk0p2 rw rootwait [ 0.000000] Dentry cache hash table entries: 1048576 (order: 11, 8388608 bytes, linear) [ 0.000000] Inode-cache hash table entries: 524288 (order: 10, 4194304 bytes, linear) [ 0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off [ 0.000000] Unable to handle kernel paging request at virtual address ffff00007fc00000 [ 0.000000] Mem abort info: [ 0.000000] ESR = 0x96000047 [ 0.000000] EC = 0x25: DABT (current EL), IL = 32 bits [ 0.000000] SET = 0, FnV = 0 [ 0.000000] EA = 0, S1PTW = 0 [ 0.000000] FSC = 0x07: level 3 translation fault [ 0.000000] Data abort info: [ 0.000000] ISV = 0, ISS = 0x00000047 [ 0.000000] CM = 0, WnR = 1 [ 0.000000] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000001378000 [ 0.000000] [ffff00007fc00000] pgd=180000097fff8003, p4d=180000097fff8003 [ 0.000000] Unable to handle kernel paging request at virtual address ffff00097fff8008 [ 0.000000] Mem abort info: [ 0.000000] ESR = 0x96000006 [ 0.000000] EC = 0x25: DABT (current EL), IL = 32 bits [ 0.000000] SET = 0, FnV = 0 [ 0.000000] EA = 0, S1PTW = 0 [ 0.000000] FSC = 0x06: level 2 translation fault [ 0.000000] Data abort info: [ 0.000000] ISV = 0, ISS = 0x00000006 [ 0.000000] CM = 0, WnR = 0 [ 0.000000] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000001378000 [ 0.000000] [ffff00097fff8008] pgd=180000097fff8003, p4d=180000097fff8003 [ 0.000000] Unable to handle kernel paging request at virtual address ffff00097fff8128 [ 0.000000] Mem abort info: [ 0.000000] ESR = 0x96000006 [ 0.000000] EC = 0x25: DABT (current EL), IL = 32 bits [ 0.000000] SET = 0, FnV = 0 [ 0.000000] EA = 0, S1PTW = 0 [ 0.000000] FSC = 0x06: level 2 translation fault [ 0.000000] Data abort info: [ 0.000000] ISV = 0, ISS = 0x00000006 [ 0.000000] CM = 0, WnR = 0 [ 0.000000] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000001378000 [ 0.000000] [ffff00097fff8128] pgd=180000097fff8003, p4d=180000097fff8003 [ 0.000000] Unable to handle kernel paging request at virtual address ffff00097fff8128 [ 0.000000] Mem abort info: [ 0.000000] ESR = 0x96000006 [ 0.000000] EC = 0x25: DABT (current EL), IL = 32 bits [ 0.000000] SET = 0, FnV = 0 [ 0.000000] EA = 0, S1PTW = 0 [ 0.000000] FSC = 0x06: level 2 translation fault [ 0.000000] Data abort info: [ 0.000000] ISV = 0, ISS = 0x00000006 [ 0.000000] CM = 0, WnR = 0 [ 0.000000] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000001378000 [ 0.000000] [ffff00097fff8128] pgd=180000097fff8003, p4d=180000097fff8003 [ 0.000000] Unable to handle kernel paging request at virtual address ffff00097fff8128 [ 0.000000] Mem abort info: [ 0.000000] ESR = 0x96000006 [ 0.000000] EC = 0x25: DABT (current EL), IL = 32 bits [ 0.000000] SET = 0, FnV = 0 [ 0.000000] EA = 0, S1PTW = 0 [ 0.000000] FSC = 0x06: level 2 translation fault [ 0.000000] Data abort info: [ 0.000000] ISV = 0, ISS = 0x00000006 [ 0.000000] CM = 0, WnR = 0 [ 0.000000] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000001378000 [ 0.000000] [ffff00097fff8128] pgd=180000097fff8003, p4d=180000097fff8003 [ 0.000000] Unable to handle kernel paging request at virtual address ffff00097fff8128 [ 0.000000] Mem abort info: [ 0.000000] ESR = 0x96000006 [ 0.000000] EC = 0x25: DABT (current EL), IL = 32 bits [ 0.000000] SET = 0, FnV = 0 [ 0.000000] EA = 0, S1PTW = 0 [ 0.000000] FSC = 0x06: level 2 translation fault [ 0.000000] Data abort info: [ 0.000000] ISV = 0, ISS = 0x00000006 [ 0.000000] CM = 0, WnR = 0 [ 0.000000] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000001378000 [ 0.000000] [ffff00097fff8128] pgd=180000097fff8003, p4d=180000097fff8003 [ 0.000000] Unable to handle kernel paging request at virtual address ffff00097fff8128 [ 0.000000] Mem abort info: [ 0.000000] ESR = 0x96000006 [ 0.000000] EC = 0x25: DABT (current EL), IL = 32 bits [ 0.000000] SET = 0, FnV = 0 [ 0.000000] EA = 0, S1PTW = 0 [ 0.000000] FSC = 0x06: level 2 translation fault [ 0.000000] Data abort info: [ 0.000000] ISV = 0, ISS = 0x00000006 [ 0.000000] CM = 0, WnR = 0 [ 0.000000] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000001378000 [ 0.000000] [ffff00097fff8128] pgd=180000097fff8003, p4d=180000097fff8003 [ 0.000000] Unable to handle kernel paging request at virtual address ffff00097fff8128 [ 0.000000] Mem abort info: [ 0.000000] ESR = 0x96000006 [ 0.000000] EC = 0x25: DABT (current EL), IL = 32 bits [ 0.000000] SET = 0, FnV = 0 [ 0.000000] EA = 0, S1PTW = 0 [ 0.000000] FSC = 0x06: level 2 translation fault [ 0.000000] Data abort info: [ 0.000000] ISV = 0, ISS = 0x00000006 [ 0.000000] CM = 0, WnR = 0 [ 0.000000] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000001378000 [ 0.000000] [ffff00097fff8128] pgd=180000097fff8003, p4d=180000097fff8003 [ 0.000000] Unable to handle kernel paging request at virtual address ffff00097fff8128 [ 0.000000] Mem abort info: [ 0.000000] ESR = 0x96000006 [ 0.000000] EC = 0x25: DABT (current EL), IL = 32 bits [ 0.000000] SET = 0, FnV = 0 [ 0.000000] EA = 0, S1PTW = 0 [ 0.000000] FSC = 0x06: level 2 translation fault [ 0.000000] Data abort info: [ 0.000000] ISV = 0, ISS = 0x00000006 [ 0.000000] CM = 0, WnR = 0 [ 0.000000] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000001378000 [ 0.000000] [ffff00097fff8128] pgd=180000097fff8003, p4d=180000097fff8003 [ 0.000000] Unable to handle kernel paging request at virtual address ffff00097fff8128 [ 0.000000] Mem abort info: [ 0.000000] ESR = 0x96000006 [ 0.000000] EC = 0x25: DABT (current EL), IL = 32 bits [ 0.000000] SET = 0, FnV = 0 [ 0.000000] EA = 0, S1PTW = 0 [ 0.000000] FSC = 0x06: level 2 translation fault [ 0.000000] Data abort info: [ 0.000000] ISV = 0, ISS = 0x00000006 [ 0.000000] CM = 0, WnR = 0 [ 0.000000] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000001378000 [ 0.000000] [ffff00097fff8128] pgd=180000097fff8003, p4d=180000097fff8003 [ 0.000000] Unable to handle kernel paging request at virtual address ffff00097fff8128 [ 0.000000] Mem abort info: [ 0.000000] ESR = 0x96000006 [ 0.000000] EC = 0x25: DABT (current EL), IL = 32 bits [ 0.000000] SET = 0, FnV = 0 [ 0.000000] EA = 0, S1PTW = 0 [ 0.000000] FSC = 0x06: level 2 translation fault [ 0.000000] Data abort info: [ 0.000000] ISV = 0, ISS = 0x00000006 [ 0.000000] CM = 0, WnR = 0 [ 0.000000] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000001378000 [ 0.000000] [ffff00097fff8128] pgd=180000097fff8003, p4d=180000097fff8003 [ 0.000000] Unable to handle kernel paging request at virtual address ffff00097fff8128 [ 0.000000] Mem abort info: [ 0.000000] ESR = 0x96000006 [ 0.000000] EC = 0x25: DABT (current EL), IL = 32 bits [ 0.000000] SET = 0, FnV = 0 [ 0.000000] EA = 0, S1PTW = 0 [ 0.000000] FSC = 0x06: level 2 translation fault [ 0.000000] Data abort info: [ 0.000000] ISV = 0, ISS = 0x00000006 [ 0.000000] CM = 0, WnR = 0 [ 0.000000] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000001378000 [ 0.000000] [ffff00097fff8128] pgd=180000097fff8003, p4d=180000097fff8003 [ 0.000000] Unable to handle kernel paging request at virtual address ffff00097fff8128 [ 0.000000] Mem abort info: [ 0.000000] ESR = 0x96000006 [ 0.000000] EC = 0x25: DABT (current EL), IL = 32 bits [ 0.000000] SET = 0, FnV = 0 [ 0.000000] EA = 0, S1PTW = 0 [ 0.000000] FSC = 0x06: level 2 translation fault [ 0.000000] Data abort info: [ 0.000000] ISV = 0, ISS = 0x00000006 [ 0.000000] CM = 0, WnR = 0 [ 0.000000] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000001378000 [ 0.000000] [ffff00097fff8128] pgd=180000097fff8003, p4d=180000097fff8003 [ 0.000000] Unable to handle kernel paging request at virtual address ffff00097fff8128 [ 0.000000] Mem abort info: [ 0.000000] ESR = 0x96000006 [ 0.000000] EC = 0x25: DABT (current EL), IL = 32 bits [ 0.000000] SET = 0, FnV = 0 [ 0.000000] EA = 0, S1PTW = 0 [ 0.000000] FSC = 0x06: level 2 translation fault [ 0.000000] Data abort info: [ 0.000000] ISV = 0, ISS = 0x00000006 [ 0.000000] CM = 0, WnR = 0 [ 0.000000] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000001378000 [ 0.000000] [ffff00097fff8128] pgd=180000097fff8003, p4d=180000097fff8003 [ 0.000000] Unable to handle kernel paging request at virtual address ffff00097fff8128 [ 0.000000] Mem abort info: [ 0.000000] ESR = 0x96000006 [ 0.000000] EC = 0x25: DABT (current EL), IL = 32 bits [ 0.000000] SET = 0, FnV = 0 [ 0.000000] EA = 0, S1PTW = 0 [ 0.000000] FSC = 0x06: level 2 translation fault [ 0.000000] Data abort info: [ 0.000000] ISV = 0, ISS = 0x00000006 [ 0.000000] CM = 0, WnR = 0 [ 0.000000] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000001378000 [ 0.000000] [ffff00097fff8128] pgd=180000097fff8003, p4d=180000097fff8003 [ 0.000000] Unable to handle kernel paging request at virtual address ffff00097fff8128 [ 0.000000] Mem abort info: [ 0.000000] ESR = 0x96000006 [ 0.000000] EC = 0x25: DABT (current EL), IL = 32 bits [ 0.000000] SET = 0, FnV = 0 [ 0.000000] EA = 0, S1PTW = 0 [ 0.000000] FSC = 0x06: level 2 translation fault [ 0.000000] Data abort info: [ 0.000000] ISV = 0, ISS = 0x00000006 [ 0.000000] CM = 0, WnR = 0 [ 0.000000] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000001378000 [ 0.000000] [ffff00097fff8128] pgd=180000097fff8003, p4d=180000097fff8003 [ 0.000000] Unable to handle kernel paging request at virtual address ffff00097fff8128 [ 0.000000] Mem abort info: [ 0.000000] ESR = 0x96000006 [ 0.000000] EC = 0x25: DABT (current EL), IL = 32 bits [ 0.000000] SET = 0, FnV = 0 [ 0.000000] EA = 0, S1PTW = 0 [ 0.000000] FSC = 0x06: level 2 translation fault [ 0.000000] Data abort info: [ 0.000000] ISV = 0, ISS = 0x00000006 [ 0.000000] CM = 0, WnR = 0 [ 0.000000] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000001378000 [ 0.000000] [ffff00097fff8128] pgd=180000097fff8003, p4d=180000097fff8003 [ 0.000000] Unable to handle kernel paging request at virtual address ffff00097fff8128 [ 0.000000] Mem abort info: [ 0.000000] ESR = 0x96000006 [ 0.000000] EC = 0x25: DABT (current EL), IL = 32 bits [ 0.000000] SET = 0, FnV = 0 [ 0.000000] EA = 0, S1PTW = 0 [ 0.000000] FSC = 0x06: level 2 translation fault [ 0.000000] Data abort info: [ 0.000000] ISV = 0, ISS = 0x00000006 [ 0.000000] CM = 0, WnR = 0 [ 0.000000] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000001378000 [ 0.000000] [ffff00097fff8128] pgd=180000097fff8003, p4d=180000097fff8003 [ 0.000000] Unable to handle kernel paging request at virtual address ffff00097fff8128 [ 0.000000] Mem abort info: [ 0.000000] ESR = 0x96000006 [ 0.000000] EC = 0x25: DABT (current EL), IL = 32 bits [ 0.000000] SET = 0, FnV = 0 [ 0.000000] EA = 0, S1PTW = 0 [ 0.000000] FSC = 0x06: level 2 translation fault [ 0.000000] Data abort info: [ 0.000000] ISV = 0, ISS = 0x00000006 [ 0.000000] CM = 0, WnR = 0 [ 0.000000] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000001378000 [ 0.000000] [ffff00097fff8128] pgd=180000097fff8003, p4d=180000097fff8003 [ 0.000000] Unable to handle kernel paging request at virtual address ffff00097fff8128 [ 0.000000] Mem abort info: [ 0.000000] ESR = 0x96000006 [ 0.000000] EC = 0x25: DABT (current EL), IL = 32 bits [ 0.000000] SET = 0, FnV = 0 [ 0.000000] EA = 0, S1PTW = 0 [ 0.000000] FSC = 0x06: level 2 translation fault [ 0.000000] Data abort info: [ 0.000000] ISV = 0, ISS = 0x00000006 [ 0.000000] CM = 0, WnR = 0 [ 0.000000] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000001378000 [ 0.000000] [ffff00097fff8128] pgd=180000097fff8003, p4d=180000097fff8003 [ 0.000000] Unable to handle kernel paging request at virtual address ffff00097fff8128 [ 0.000000] Mem abort info: [ 0.000000] ESR = 0x96000006 [ 0.000000] EC = 0x25: DABT (current EL), IL = 32 bits [ 0.000000] SET = 0, FnV = 0 [ 0.000000] EA = 0, S1PTW = 0 [ 0.000000] FSC = 0x06: level 2 translation fault [ 0.000000] Data abort info: [ 0.000000] ISV = 0, ISS = 0x00000006 [ 0.000000] CM = 0, WnR = 0 [ 0.000000] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000001378000 [ 0.000000] [ffff00097fff8128] pgd=180000097fff8003, p4d=180000097fff8003 [ 0.000000] Unable to handle kernel paging request at virtual address ffff00097fff8128 [ 0.000000] Mem abort info: [ 0.000000] ESR = 0x96000006 [ 0.000000] EC = 0x25: DABT (current EL), IL = 32 bits [ 0.000000] SET = 0, FnV = 0 [ 0.000000] EA = 0, S1PTW = 0 [ 0.000000] FSC = 0x06: level 2 translation fault [ 0.000000] Data abort info: [ 0.000000] ISV = 0, ISS = 0x00000006 [ 0.000000] CM = 0, WnR = 0 [ 0.000000] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000001378000 [ 0.000000] [ffff00097fff8128] pgd=180000097fff8003, p4d=180000097fff8003 [ 0.000000] Unable to handle kernel paging request at virtual address ffff00097fff8128 [ 0.000000] Mem abort info: [ 0.000000] ESR = 0x96000006 [ 0.000000] EC = 0x25: DABT (current EL), IL = 32 bits [ 0.000000] SET = 0, FnV = 0 [ 0.000000] EA = 0, S1PTW = 0 [ 0.000000] FSC = 0x06: level 2 translation fault [ 0.000000] Data abort info: [ 0.000000] ISV = 0, ISS = 0x00000006 [ 0.000000] CM = 0, WnR = 0 [ 0.000000] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000001378000 [ 0.000000] [ffff00097fff8128] pgd=180000097fff8003, p4d=180000097fff8003 [ 0.000000] Unable to handle kernel paging request at virtual address ffff00097fff8128 [ 0.000000] Mem abort info: [ 0.000000] ESR = 0x96000006 [ 0.000000] EC = 0x25: DABT (current EL), IL = 32 bits [ 0.000000] SET = 0, FnV = 0 [ 0.000000] EA = 0, S1PTW = 0 [ 0.000000] FSC = 0x06: level 2 translation fault [ 0.000000] Data abort info: [ 0.000000] ISV = 0, ISS = 0x00000006 [ 0.000000] CM = 0, WnR = 0 [ 0.000000] swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000001378000 [ 0.000000] [ffff00097fff8128] pgd=180000097fff8003, p4d=180000097fff8003 [ 0.000000] Insufficient stack space to handle exception! [ 0.000000] ESR: 0x96000006 -- DABT (current EL) [ 0.000000] FAR: 0xffff00097fff8128 [ 0.000000] Task stack: [0xffff8000093a0000..0xffff8000093a4000] [ 0.000000] IRQ stack: [0x0000000000000000..0x0000000000004000] [ 0.000000] Overflow stack: [0xffff00097ef6c2b0..0xffff00097ef6d2b0] [ 0.000000] CPU: 0 PID: 0 Comm: swapper Not tainted 5.15.36-xilinx-v2022.2 #1 [ 0.000000] Hardware name: xlnx,zynqmp (DT) [ 0.000000] pstate: 600000c5 (nZCv daIF -PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 0.000000] pc : show_pte+0x18c/0x238 [ 0.000000] lr : show_pte+0x15c/0x238 [ 0.000000] sp : ffff8000093a0020 [ 0.000000] x29: ffff8000093a0020 x28: ffff8000093b4e80 x27: 000000007fc8d978 [ 0.000000] x26: 0000000000000000 x25: 0000000000000000 x24: ffff00097efb6f80 [ 0.000000] x23: ffff800009107000 x22: ffff800009178000 x21: 0000000000000128 [ 0.000000] x20: ffff00097fff8000 x19: ffff00097fff8128 x18: ffffffffffffffff [ 0.000000] x17: 3030666666662073 x16: 736572646461206c x15: ffff8000094a7030 [ 0.000000] x14: 0000000000000010 x13: ffff8000093c6128 x12: 00000000000003ba [ 0.000000] x11: 000000000000013e x10: ffff8000093c6128 x9 : ffff8000093c6128 [ 0.000000] x8 : 00000000fffff7ff x7 : ffff8000093f2128 x6 : ffff8000093f2128 [ 0.000000] x5 : 0000000000005ff4 x4 : 40000000fffff93e x3 : 0000000000000000 [ 0.000000] x2 : 0000000000000000 x1 : 0000000000000000 x0 : ffff8000090037e8 [ 0.000000] Kernel panic - not syncing: kernel stack overflow zynqmp板卡内核打印这个错误如何解决
最新发布
07-03
<think>我们正在解决ZynqMP板卡启动时出现的内核栈溢出(&#39;kernel stackoverflow&#39;)错误导致内核崩溃的问题。根据之前提供的解决思路,结合ZynqMP平台特性,以下是具体解决方案: ###1. **增大内核线程栈大小(立即缓解措施)**ZynqMP默认内核线程栈为16KB(ARM64架构),在复杂驱动初始化时可能不足。修改方法:```bashpetalinux-config-ckernel```进入配置菜单:-定位到`Kernel Features` →`Threadstack size(KB)`,将值从16改为32(或64)-保存退出后重新编译:```bashpetalinux-build```###2.**诊断栈溢出根源****步骤1:捕获崩溃现场**-在U-Boot启动参数中添加`earlyconloglevel=8`:```bashsetenvbootargs"earlycon loglevel=8"saveenv```-从串口日志中定位崩溃前的最后调用栈(关键信息如`Calltrace:`)**步骤2:分析溢出函数**若日志显示类似:```Kernel panic- notsyncing: kernelstack overflow:0xffffffc008e2ffb8-0xffffffc008e30000Call trace:[<ffffff800808f1d4>] dump_backtrace+0x0/0x1a0[<ffffff8008090c58>]show_stack+0x18/0x70```则需关注栈回溯中重复出现的函数(通常是递归或大局部变量)。**步骤3:针对性修复**- **案例A:驱动递归调用**常见于中断处理中重复触发中断。修复方法:在ISR中及时清除中断标志。- **案例B:大尺寸局部数组**如函数内定义`charbuffer[8192];`(占8KB栈空间)。修改为动态分配:```c//原代码voidfoo(){char buffer[8192];//危险!}//修改后void foo() {char*buffer= kmalloc(8192, GFP_KERNEL);//使用buffer...kfree(buffer);}```###3.**启用栈保护机制(主动防御)**在Petalinux内核配置中启用栈溢出检测:```bashpetalinux-config-ckernel```开启选项:-`CONFIG_STACKPROTECTOR=y`(基本保护)- `CONFIG_STACKPROTECTOR_STRONG=y`(强保护)- `CONFIG_DEBUG_STACK_USAGE=y`(栈使用统计) ###4. **ZynqMP多核同步问题排查**若栈溢出发生在从核(CPU1~CPU3启动阶段:- **临时禁用从核**:在设备树`project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi`中添加:```dts/ {cpus {cpu@1{status ="disabled"; };cpu@2{ status= "disabled";};cpu@3 {status ="disabled"; };};};```-若单核启动正常,则问题出在多核同步:-检查PMU固件版本(需与Petalinux版本匹配)-验证IPI(核间中断)设备树配置###5.**硬件相关栈溢出(DDR配置)**ZynqMP启动阶段DDR初始化错误可能引发伪栈溢出:-使用XilinxVitis检查DDR控制器配置(尤其是`psu_ddr_*`寄存器)-在FSBL阶段启用DDR ECC校验(若使用ECC内存)>**技术点**:Linux内核线程栈与进程用户栈分离,默认大小由编译选项确定[^1]。ZynqMP的Cortex-A53核使用`SP_EL0`作为用户栈指针,`SP_EL1`作为内核栈指针,溢出会触发`SP_EL1`异常[^2]。 ###最终验证流程1.编译新镜像并启动:```bashpetalinux-buildpetalinux-package--boot--fsbl images/linux/zynqmp_fsbl.elf --u-boot```2.通过串口观察启动日志,确认`kernel stackoverflow`消失。§§相关问题§§1.如何永久修改Petalinux工程中的内核线程栈大小? 2. ZynqMP多核启动时如何调试核间通信(IPI)问题?3.Linux内核栈溢出诊断工具有哪些? 4. Petalinux中如何调整DDR控制器参数?5.Cortex-A53架构中SP_EL0和SP_EL1的区别是什么?[^1]:Linux内核线程栈实现机制[^2]: ARMv8异常级别与栈指针寄存器设计</think>### 解决ZynqMP板卡内核栈溢出崩溃问题 针对ZynqMP板卡启动时出现的"kernel stack overflow"错误,以下是系统化的解决方案: #### 1. **增大内核线程栈大小(最直接方案)** ```bash # 修改内核配置 petalinux-config -c kernel ``` 在配置菜单中: - 导航到 `Kernel Features` → `Thread stack size (KB)` - 将默认值 **16** 改为 **32** 或 **64**(ARM64架构最大支持64KB) - 保存后重新编译: ```bash petalinux-build ``` > **原理**:ZynqMP的Cortex-A53内核默认16KB栈空间,复杂驱动初始化时可能不足[^1]。 #### 2. **诊断栈溢出根源** **步骤1:捕获崩溃现场** - 在U-Boot中修改启动参数: ```bash setenv bootargs "earlycon loglevel=8 panic=10" saveenv ``` - 从串口日志中定位崩溃前的最后调用栈(关键字段:`Call trace:`) **步骤2:分析可疑函数** 常见问题模式: - **递归函数失控**:检查驱动中未正确退出的递归逻辑 - **大尺寸局部变量**:如`char buffer[8192];`(占50%栈空间) - **深度调用链**:特别是中断处理函数嵌套 **步骤3:启用栈保护** ```bash # 内核配置中启用 CONFIG_STACKPROTECTOR=y CONFIG_STACKPROTECTOR_STRONG=y CONFIG_DEBUG_STACK_USAGE=y # 栈使用统计 ``` #### 3. **ZynqMP特定优化** **多核同步问题处理**: ```dts // system-user.dtsi 中添加 /cpus/cpu@1 { status = "disabled"; // 先禁用从核测试 }; ``` **PMU固件验证**: ```bash # 检查PMU版本兼容性 petalinux-boot --jtag --pmufw images/linux/pmufw.elf ``` #### 4. **内存配置检查** ZynqMP DDR配置错误会导致伪栈溢出: ```bash # 在FSBL阶段验证DDR初始化 petalinux-boot --jtag --prebuilt 3 --hw_server-url tcp:localhost:3121 ``` 关键参数: - DDR控制器时序(`psu_ddr_*`寄存器) - 内存映射冲突(检查`mem=xxxM`启动参数) #### 5. **高级调试方法** **JTAG栈分析**: 1. 启动到U-Boot后暂停 2. 加载内核镜像到内存: ```bash petalinux-boot --jtag --kernel --initrd ``` 3. 在GDB中设置断点: ```gdb b panic if strstr(msg, "stack overflow") ``` **栈使用监控**: ```bash # 运行后查看栈使用峰值 cat /proc/$(pidof kthreadd)/stack ``` ### 典型修复案例 **场景**:PL端自定义IP驱动导致溢出 **修复**: ```c // 修改前:大数组在栈上 void isr_handler() { char buffer[16384]; // 危险! } // 修改后:动态分配 void isr_handler() { char *buffer = kmalloc(16384, GFP_ATOMIC); // ... kfree(buffer); } ``` > **关键点**:ZynqMP启动时所有驱动初始化共享同一栈空间,复杂FPGA设计需特别注意[^2]。 ### 验证流程 ```mermaid graph TD A[增大栈大小至32KB] --> B{启动成功?} B -->|是| C[问题解决] B -->|否| D[捕获崩溃日志] D --> E[分析调用栈] E --> F[修改问题函数] F --> G[重新编译测试] ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值