Linux内核源码分析--内核启动之(4)Image内核启动(setup_arch函数)(Linux-3.0 ARMv7)

简介: 在分析start_kernel函数的时候,其中有构架相关的初始化函数setup_arch。此函数根据构架而异,对于ARM构架的详细分析如下:void __init setup_arch(char **cmdline_p){    struct machine_desc ...
在分析start_kernel函数的时候,其中有构架相关的初始化函数setup_arch。
此函数根据构架而异,对于ARM构架的详细分析如下:

  1. void __init setup_arch(char **cmdline_p)
  2. {
  3.     struct machine_desc *mdesc;

  4. 点击(此处)折叠或打开

    1. 此为设备描述结构体,对于任何板子都定义了这样的一个结构体,我以前的文章有介绍:
    2. 《Uncompressing Linux... done, booting the kernel》 1、machine type 不匹配

  5.     unwind_init();
  6. 点击(此处)折叠或打开

    1. 初始化基於ARM EABI的Backtrace Unwind機制(栈回退),此函数主要用于地址转换(arch/arm/kernel/unwind.c)

  7.     setup_processor();

    点击(此处)折叠或打开

    1. 再次检测处理器类型,并初始化处理器相关的底层变量。内核启动时的处理器信息(包括cache)就是通过这个函数打印的,例如:
      1. CPU: ARMv7 Processor [413fc082] revision 2 (ARMv7), cr=10c53c7f
      2. CPU: VIPT nonaliasing data cache, VIPT aliasing instruction cache

  8.     mdesc = setup_machine_fdt(__atags_pointer);
  9.     if (!mdesc)
  10.         mdesc = setup_machine_tags(machine_arch_type);

    点击(此处)折叠或打开

    1. 在此处通过bootloader传递过来的设备ID来匹配一个 struct machine_desc 结构体
    2. (这个结构体就是在arch/arm/mach-*/mach-*.c中定义的结构体:MACHINE_START~MACHINE_END )
    3. 如果没有匹配上就死循环。
    4. 如果匹配上了就打印机器名 ,并处理bootloader传递过来的tagged_list,将所有的tag信息保存到相应的全局变量或结构体中。
    5. 内核启动时的机器信息就是这里打印的,例如:
    6. 点击(此处)折叠或打开

      1. Machine: ti8168evm
    7. 最后返回结构体指针。

  11.     machine_desc = mdesc;
  12.     machine_name = mdesc->name;

    点击(此处)折叠或打开

    1. 通过匹配的struct machine_desc 结构体数据,初始化一些全局变量

  13.     if (mdesc->soft_reboot)
  14.         reboot_setup("s");

    点击(此处)折叠或打开

    1. 通过struct machine_desc 中的soft_reboot数据来设置重启类型:
    2. 如果存在就为“s”:softreset;如果不存在就为“h”:hardreset

  15.     init_mm.start_code = (unsigned long) _text;
  16.     init_mm.end_code = (unsigned long) _etext;
  17.     init_mm.end_data = (unsigned long) _edata;
  18.     init_mm.brk     = (unsigned long) _end;

    点击(此处)折叠或打开

    1. 这里通过连接脚本中得到的Linux代码位置数据来初始化一个mm_struct结构体init_mm中的部分数据
    2. ps:每一个任务都有一个mm_struct结构以管理内存空间,init_mm是内核自身的mm_struct

  19.     /* 同时填充cmd_line以备后用, 保护boot_command_line数据 */
  20.     strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
  21.     *cmdline_p = cmd_line;

    点击(此处)折叠或打开

    1. 将boot_command_line复制到cmd_line中。这里关键是要知道系统启动的时候的cmdline是如何传递的。

  22.     parse_early_param();

    点击(此处)折叠或打开

    1. 处理在 struct obs_kernel_param 中定义为early的启动参数(主要是内存配置部分的参数)

    2. 其中就分析了mem=size@start参数初始化了struct meminfo meminfo;
    3. 同时如果有vmalloc=size参数也会初始化 vmalloc_min
    4. 参考:《Linux内核高-低端内存设置代码跟踪(ARM构架)》

    5. 这里需要注意的是内核的cmdline中的参数按照其被需要的先后,分为early和非early的。

    6. include/linux/init.h:

      点击(此处)折叠或打开

      1. struct obs_kernel_param {
      2. const char *str;            //在cmdline中相应参数名。
      3. int (*setup_func)(char *);  //对于此参数的专用处理函数
      4. int early;                  //是否为早期需要处理的参数
      5. };
    7. 两种不同的参数在内核中用了不同的宏来定义:
    8. early: #define early_param(str, fn) \
    9.         __setup_param(str, fn, fn, 1)
    10. 非early: #define __setup(str, fn) \
    11.          __setup_param(str, fn, fn, 0)
    12. 使用这两个宏定义的参数和构架相关,一些构架或者板子可以定义自己特定的参数和处理函数。对于比较重要的“men”参数就是early参数。

  23.     sanity_check_meminfo();

    点击(此处)折叠或打开

    1. 在此处设置struct meminfo meminfo中每个bank中的highmem变量,
    2. 通过vmalloc_min确定每个bank中的内存是否属于高端内存
  24.     arm_memblock_init(&meminfo, mdesc);

    点击(此处)折叠或打开

    1. 在此处按地址数据从小到大排序meminfo中的数据,并初始化全局的memblock数据。

  25.     paging_init(mdesc);

    点击(此处)折叠或打开

    1. 设置内核的参考页表。
    2. 此页表不仅用于物理内存映射,还用于管理vmalloc区。
    3. 此函数中非常重要的一点就是初始化了bootmem分配器!


  26.     request_standard_resources(mdesc);

  27. 点击(此处)折叠或打开

    1. 通过获取设备描述结构体(struct machine_desc)中的数据和编译时产生的地址数据,初始化内存相关的全局结构体变量
  28.     unflatten_device_tree();

    点击(此处)折叠或打开

    1. 通过启动参数中的“非平坦设备树”信息(如果有),获取内存相关信息

  29. #ifdef CONFIG_SMP
  30.     if (is_smp())
  31.         smp_init_cpus();
  32. #endif

    点击(此处)折叠或打开

    1. 针对SMP处理器,初始化可能存在的CPU映射 - 这描述了可能存在的CPU

  33.     reserve_crashkernel();

    点击(此处)折叠或打开

    1. 用于内核崩溃时的保留内核
    2. 此功能通过内核command line参数中的"crashkernel="保留下内存用于主内核崩溃时获取内核信息的导出。 

  34.     cpu_init();

    点击(此处)折叠或打开

    1. 初始化一个CPU,并设置一个per-CPU栈

  35.     tcm_init();

    点击(此处)折叠或打开

    1. 初始化ARM内部的TCM(紧耦合内存)。
    2. 参考资料:《对ARM紧致内存TCM的理解》
    3. ARM官网也有介绍文档

  36. #ifdef CONFIG_MULTI_IRQ_HANDLER
  37.     handle_arch_irq = mdesc->handle_irq;
  38. #endif

  39. 点击(此处)折叠或打开

    1. 调用设备描述结构体中的mdesc->handle_irq函数,目的未知。

  40. #ifdef CONFIG_VT
  41. #if defined(CONFIG_VGA_CONSOLE)
  42.     conswitchp = &vga_con;
  43. #elif defined(CONFIG_DUMMY_CONSOLE)
  44.     conswitchp = &dummy_con;
  45. #endif
  46. #endif
  47.     early_trap_init();

  48. 点击(此处)折叠或打开

    1. 对中断向量表进行早期初始化

  49.     if (mdesc->init_early)
  50.         mdesc->init_early();

    点击(此处)折叠或打开

    1. 如果设备描述结构体定义了init_early函数(应该是早期初始化之意),则在这里调用。

  51. }


这个函数主要是检查处理器的类型是否匹配,并获取处理器信息来设置处理器的相关底层参数。
  1. static void __init setup_processor(void)
  2. {
  3.     struct proc_info_list *list;

  4.     /*
  5.      在支持处理器列表中定位处理器
  6.    * 连接器为我们创建这个列表,从                                                                              * arch/arm/mm/proc-*.S中的入口
  7.     */
  8.     list = lookup_processor_type(read_cpuid_id());
  9.     if (!list) {
  10.         printk("CPU configuration botched (ID %08x), unable "
  11.          "to continue.\n", read_cpuid_id());
  12.         while (1);
  13.     }

    点击(此处)折叠或打开

    1. 这里再次核对处理器类型,虽然这个已经在汇编代码中执行过一遍了

  14.     cpu_name = list->cpu_name;

  15. #ifdef MULTI_CPU
  16.     processor = *list->proc;
  17. #endif
  18. #ifdef MULTI_TLB
  19.     cpu_tlb = *list->tlb;
  20. #endif
  21. #ifdef MULTI_USER
  22.     cpu_user = *list->user;
  23. #endif
  24. #ifdef MULTI_CACHE
  25.     cpu_cache = *list->cache;
  26. #endif

  27. 点击(此处)折叠或打开

    1. 通过从struct proc_info_list获取的数据初始化CPU相关的全局变量

  28.     printk("CPU: %s [%08x] revision %d (ARMv%s), cr=%08lx\n",
  29.      cpu_name, read_cpuid_id(), read_cpuid_id() & 15,
  30.      proc_arch[cpu_architecture()], cr_alignment);

  31. 点击(此处)折叠或打开

    1. 打印内核启动时的处理器信息

  32.     sprintf(init_utsname()->machine, "%s%c", list->arch_name, ENDIANNESS);
  33.     sprintf(elf_platform, "%s%c", list->elf_name, ENDIANNESS);
  34.     elf_hwcap = list->elf_hwcap;
  35. #ifndef CONFIG_ARM_THUMB
  36.     elf_hwcap &= ~HWCAP_THUMB;
  37. #endif

  38.     feat_v6_fixup();
  39. 点击(此处)折叠或打开

    1. 针对特定的ARM核软件屏蔽一些功能

  40.     cacheid_init();

    点击(此处)折叠或打开

    1. 初始化ARM核中的缓存
  41.     cpu_proc_init();

    点击(此处)折叠或打开

    1. 宏:
    2. #define cpu_proc_init __glue(CPU_NAME,_proc_init)
    3. 意在调用处理器特定的初始化函数。
  42. }



相关文章
|
5天前
|
存储 Linux
linux中的目录操作函数
本文详细介绍了Linux系统编程中常用的目录操作函数,包括创建目录、删除目录、读取目录内容、遍历目录树以及获取和修改目录属性。这些函数是进行文件系统操作的基础,通过示例代码展示了其具体用法。希望本文能帮助您更好地理解和应用这些目录操作函数,提高系统编程的效率和能力。
52 26
|
1月前
|
安全 Linux 测试技术
Intel Linux 内核测试套件-LKVS介绍 | 龙蜥大讲堂104期
《Intel Linux内核测试套件-LKVS介绍》(龙蜥大讲堂104期)主要介绍了LKVS的定义、使用方法、测试范围、典型案例及其优势。LKVS是轻量级、低耦合且高代码覆盖率的测试工具,涵盖20多个硬件和内核属性,已开源并集成到多个社区CICD系统中。课程详细讲解了如何使用LKVS进行CPU、电源管理和安全特性(如TDX、CET)的测试,并展示了其在实际应用中的价值。
|
1月前
|
Ubuntu Linux 开发者
Ubuntu20.04搭建嵌入式linux网络加载内核、设备树和根文件系统
使用上述U-Boot命令配置并启动嵌入式设备。如果配置正确,设备将通过TFTP加载内核和设备树,并通过NFS挂载根文件系统。
100 15
|
2月前
|
Linux
【Linux】System V信号量详解以及semget()、semctl()和semop()函数讲解
System V信号量的概念及其在Linux中的使用,包括 `semget()`、`semctl()`和 `semop()`函数的具体使用方法。通过实际代码示例,演示了如何创建、初始化和使用信号量进行进程间同步。掌握这些知识,可以有效解决多进程编程中的同步问题,提高程序的可靠性和稳定性。
91 19
|
2月前
|
Linux Android开发 开发者
linux m、mm、mmm函数和make的区别
通过理解和合理使用这些命令,可以更高效地进行项目构建和管理,特别是在复杂的 Android 开发环境中。
82 18
|
2月前
|
存储 监控 Linux
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
121 13
|
2月前
|
算法 Linux
深入探索Linux内核的内存管理机制
本文旨在为读者提供对Linux操作系统内核中内存管理机制的深入理解。通过探讨Linux内核如何高效地分配、回收和优化内存资源,我们揭示了这一复杂系统背后的原理及其对系统性能的影响。不同于常规的摘要,本文将直接进入主题,不包含背景信息或研究目的等标准部分,而是专注于技术细节和实际操作。
|
2月前
|
存储 缓存 网络协议
Linux操作系统的内核优化与性能调优####
本文深入探讨了Linux操作系统内核的优化策略与性能调优方法,旨在为系统管理员和高级用户提供一套实用的指南。通过分析内核参数调整、文件系统选择、内存管理及网络配置等关键方面,本文揭示了如何有效提升Linux系统的稳定性和运行效率。不同于常规摘要仅概述内容的做法,本摘要直接指出文章的核心价值——提供具体可行的优化措施,助力读者实现系统性能的飞跃。 ####
|
2月前
|
缓存 监控 网络协议
Linux操作系统的内核优化与实践####
本文旨在探讨Linux操作系统内核的优化策略与实际应用案例,深入分析内核参数调优、编译选项配置及实时性能监控的方法。通过具体实例讲解如何根据不同应用场景调整内核设置,以提升系统性能和稳定性,为系统管理员和技术爱好者提供实用的优化指南。 ####
|
2月前
|
监控 算法 Linux
Linux内核锁机制深度剖析与实践优化####
本文作为一篇技术性文章,深入探讨了Linux操作系统内核中锁机制的工作原理、类型及其在并发控制中的应用,旨在为开发者提供关于如何有效利用这些工具来提升系统性能和稳定性的见解。不同于常规摘要的概述性质,本文将直接通过具体案例分析,展示在不同场景下选择合适的锁策略对于解决竞争条件、死锁问题的重要性,以及如何根据实际需求调整锁的粒度以达到最佳效果,为读者呈现一份实用性强的实践指南。 ####