linux 内核移植(六)——C语言启动部分分析(一)

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介:

一:来源

前面分析,内核启动是从hesd.S开始的,在建立段式页表之后,

ldr r13, __switch_data 跳转到__switch_data

1
2
3
4
5
6
7
8
9
10
11
__switch_data:
.long__mmap_switched
.long__data_loc@ r4
.long_data@ r5
.long__bss_start@ r6
.long_end@ r7
.longprocessor_id@ r4
.long__machine_arch_type@ r5
.long__atags_pointer@ r6
.longcr_alignment@ r7
.longinit_thread_union + THREAD_START_SP @ sp


这个东西其实可以理解为一个函数指针数组,并且做了如下事情

(1)分析得知下一步要执行__mmap_switched函数,在这个函数中通过

b start_kernel跳转到C语言运行阶段。

(2)复制数据段、清除bss段(目的是构建C语言运行环境)

(3)保存起来cpu id号、机器码、tag传参的首地址。


二:C语言运行阶段分析

(1)函数名:start_kernel();这个和uboot启动第二阶段的函数名start_armboot很像,原因是uboot本身就是模仿kernel写出来的。

(2)smp_setup_processor_id();

SMP (对称多处理器)。对单核SOC来说,mpidr = 0;所以当只有一个CPU时这个函数就什么也不做,但如果有多个CPU的时候那么它就返回在启动的时候那个CPU的ID号

(3)lockdep_init();    

初始化lockdep hash表

lockdep: 死锁检测模块

死锁是指多个进程(线程)因为长久等待已被其他进程占有的的资源而陷入阻塞的一种状态。当等待的资源一直得不到释放,死锁会一直持续下去。死锁一旦发生,程序本身是解决不了的,只能依靠外部力量使得程序恢复运行,例如重启,开门狗复位等。

(4)debug_objects_early_init();  //初始化debug kernel相关

(5)boot_init_stack_canary();

stack_canary的是带防止栈溢出攻击保护的堆栈。详见http://www.cloud-sec.org/CC_STACKPROTECTOR_patch_Analysis

(6)cgroup_init_early();

Cgroup初始化,Cgroup是近代linux kernel出现的.它为进程和其后续的子进程提供了一种性能控制机制,详见:http://linux.chinaunix.net/techdoc/net/2008/12/23/1054425.shtml

(7)local_irq_disable();  

     关闭当前CPU的中断

(8)early_boot_irqs_off();  

也是和CPU中断相关的

(9)early_init_irq_lock_class();  

IRQ中断的初始化

(10)lock_kernel();

(11)tick_init();   //初始化 tick控制功能,注册clockevents的框架

(12)boot_cpu_init();

    对于CPU核的系统来说,设置第一个CPU核为活跃  CPU核。对于单CPU核系统来说,设置CPU核为活跃CPU核

(13)page_address_init();

    当定义了CONFIG_HIGHMEM 宏,并且没有定义   WANT_PAGE_VIRTUAL 宏时,非空函数。其他情况为空函数。注意:ARM9不支持高端地址(大于896M),一般的嵌入式产品也不会用高端地址,所以,在ARM体系结构下,此函数为空

(14)printk(KERN_NOTICE "%s", linux_banner);

    将linux_banner的内容打印到log_buf缓冲区中去,等到串口或者其它终端初始化之后,在一次性打印到终端上去 KERN_NOTICE这个宏表示打印的级别,只有打印级别高于(等于可能也行)控制台的打印级别的信息最终才能被打印出来

linux_banner是linux内核的信息,主要是版本,编译生成的时间等

(15)setup_arch(&command_line);

     实际上这个函数是用来确定我们当前内核的机器(arch、machine)的。

我们的linux内核会支持一种CPU的运行,CPU+开发板就确定了一个硬件平台,

然后我们当前配置的内核就在这个平台上可以运行。之前说过的机器码就是给这个硬件平台一个固定的编码,以表征这个平台。当前内核支持的机器码以及硬件平台相关的一些定义都在这个函数中处理。


这个函数里面的调用的函数分析

   setup_processor函数用来查找CPU信息,可以结合串口打印的信息来分析。


   setup_machine函数的传参是机器码编号,machine_arch_type符号在include/generated/mach-types.h的32039-32050行定义了。经过分析后确定这个传参值就是2456.函数的作用是通过传入的机器码编号,找到对应这个机器码的machine_desc描述符,并且返回这个描述符的指针。其实真正干活的函数是lookup_machine_type,找这个函数发现在head-common.S中,真正干活的函数是__lookup_machine_type。)__lookup_machine_type函数的工作原理:内核在建立的时候就把各种CPU架构的信息组织成一个一个的machine_desc结构体实例,然后都给一个段属性.arch.info.init,链接的时候会保证这些描述符会被连接在一起。__lookup_machine_type就去那个那些描述符所在处依次挨个遍历各个描述符,比对看机器码哪个相同。

setup_arch函数进行了基本的cmdline处理

(1)这里说的cmdline就是指的uboot给kernel传参时传递的命令行启动参数,也就是uboot的bootargs

(2)有几个相关的变量需要注意:

default_command_line:看名字是默认的命令行参数,实际是一个全局变量字符数组,这个字符数组可以用来存东西。

CONFIG_CMDLINE:在.config文件中定义的(可以在make menuconfig中去更改设置),这个表示内核的一个默认的命令行参数。

(3)内核对cmdline的处理思路是:内核中自己维护了一个默认的cmdline(就是.config中配置的这一个),然后uboot还可以通过tag给kernel再传递一个cmdline。如果uboot给内核传cmdline成功则内核会优先使用uboot传递的这一个;如果uboot没有给内核传cmdline或者传参失败,则内核会使用自己默认的这个cmdline。以上说的这个处理思路就是在setup_arch函数中实现的


实验验证内核的cmdline确定

(1)验证思路:首先给内核配置时配置一个基本的cmdline,然后在uboot启动内核时给uboot设置一个bootargs,然后启动内核看打印出来的cmdline和uboot传参时是否一样。

(2)在uboot中去掉bootargs,然后再次启动内核看打印出来的cmdline是否和内核中设置的默认的cmdline一样。


注意:uboot给内核传递的cmdline非常重要,会影响内核的运行,所以要谨慎。有时候内核启动有问题,可以分析下是不是uboot的bootargs设置不对。

总结:setup_arch中做的2件事情:机器码架构的查找并且执行架构相关的硬件的初始化、uboot给内核的传参cmdline。


(16)mm_init_owner(&init_mm, &init_task);  

内存管理相关的初始化

(17)setup_command_line(command_line);

command 相关的函数处理命令行先关的函数,给saved_command_line和static_command_line分配内存;复制boot_command_line和command_line,这两东西其实就是setup_machine中的default_command_line中的内容,或者是uboot传参时的ubootargs

(18)printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line);

正式打印kernel command line的信息

(19)parse_early_param和parse_args

解析cmdline传参和其他传参,也就是把cmdline的信息以及细节解析出来。譬如cmdline :console=ttySAC2,115200 root=/dev/mmcblk0p2 rw init=/linuxrc rootfstype=ext3,则解析出的内容就是就是一个字符串数组,数组中依次存放了一个设置项目信息。

console=ttySAC2,115200   一个

oot=/dev/mmcblk0p2 rw      一个

init=/linuxrc                              一个

rootfstype=ext3                           一个

也就是将cmdline解析成四个字符串,每个字符串将来都对应一个设置项,每个设置项都会对kernel的运行有所影响。这里只是进行了解析,并没有去处理。也就是说只是把长字符串解析成了短字符串,最多和内核里控制这个相应功能的变量挂钩了,但是并没有去执行。执行的代码在各自模块初始化的代码部分。


(20)trap_init       设置异常向量表

(21)mm_init 内存管理模块初始化

(22)sched_init         内核调度系统初始化

(23)early_irq_init&init_IRQ          中断初始化

(24)console_init   控制台初始化


总结:start_kernel函数中调用了很多的xx_init函数,全都是内核工作需要的模块的初始化函数。这些初始化之后内核就具有了一个基本的可以工作的条件了。

如果把内核比喻成一个复杂机器,那么start_kernel函数就是把这个机器的众多零部件组装在一起形成这个机器,让他具有可以工作的基本条件。

当这些部分都初始化完成以后,运行到了start_kernel的最后一个函数rest_init()


本文转自 菜鸟养成记 51CTO博客,原文链接:http://blog.51cto.com/11674570/1840894
相关文章
|
2月前
|
安全 Linux 测试技术
Intel Linux 内核测试套件-LKVS介绍 | 龙蜥大讲堂104期
《Intel Linux内核测试套件-LKVS介绍》(龙蜥大讲堂104期)主要介绍了LKVS的定义、使用方法、测试范围、典型案例及其优势。LKVS是轻量级、低耦合且高代码覆盖率的测试工具,涵盖20多个硬件和内核属性,已开源并集成到多个社区CICD系统中。课程详细讲解了如何使用LKVS进行CPU、电源管理和安全特性(如TDX、CET)的测试,并展示了其在实际应用中的价值。
|
2月前
|
Ubuntu Linux 开发者
Ubuntu20.04搭建嵌入式linux网络加载内核、设备树和根文件系统
使用上述U-Boot命令配置并启动嵌入式设备。如果配置正确,设备将通过TFTP加载内核和设备树,并通过NFS挂载根文件系统。
137 15
|
4月前
|
负载均衡 算法 Linux
深入探索Linux内核调度器:公平与效率的平衡####
本文通过剖析Linux内核调度器的工作机制,揭示了其在多任务处理环境中如何实现时间片轮转、优先级调整及完全公平调度算法(CFS),以达到既公平又高效地分配CPU资源的目标。通过对比FIFO和RR等传统调度策略,本文展示了Linux调度器如何在复杂的计算场景下优化性能,为系统设计师和开发者提供了宝贵的设计思路。 ####
93 26
|
3月前
|
算法 Linux
深入探索Linux内核的内存管理机制
本文旨在为读者提供对Linux操作系统内核中内存管理机制的深入理解。通过探讨Linux内核如何高效地分配、回收和优化内存资源,我们揭示了这一复杂系统背后的原理及其对系统性能的影响。不同于常规的摘要,本文将直接进入主题,不包含背景信息或研究目的等标准部分,而是专注于技术细节和实际操作。
|
3月前
|
存储 缓存 网络协议
Linux操作系统的内核优化与性能调优####
本文深入探讨了Linux操作系统内核的优化策略与性能调优方法,旨在为系统管理员和高级用户提供一套实用的指南。通过分析内核参数调整、文件系统选择、内存管理及网络配置等关键方面,本文揭示了如何有效提升Linux系统的稳定性和运行效率。不同于常规摘要仅概述内容的做法,本摘要直接指出文章的核心价值——提供具体可行的优化措施,助力读者实现系统性能的飞跃。 ####
|
3月前
|
监控 算法 Linux
Linux内核锁机制深度剖析与实践优化####
本文作为一篇技术性文章,深入探讨了Linux操作系统内核中锁机制的工作原理、类型及其在并发控制中的应用,旨在为开发者提供关于如何有效利用这些工具来提升系统性能和稳定性的见解。不同于常规摘要的概述性质,本文将直接通过具体案例分析,展示在不同场景下选择合适的锁策略对于解决竞争条件、死锁问题的重要性,以及如何根据实际需求调整锁的粒度以达到最佳效果,为读者呈现一份实用性强的实践指南。 ####
|
3月前
|
缓存 监控 网络协议
Linux操作系统的内核优化与实践####
本文旨在探讨Linux操作系统内核的优化策略与实际应用案例,深入分析内核参数调优、编译选项配置及实时性能监控的方法。通过具体实例讲解如何根据不同应用场景调整内核设置,以提升系统性能和稳定性,为系统管理员和技术爱好者提供实用的优化指南。 ####
|
3月前
|
负载均衡 算法 Linux
深入探索Linux内核调度机制:公平与效率的平衡####
本文旨在剖析Linux操作系统内核中的进程调度机制,特别是其如何通过CFS(完全公平调度器)算法实现多任务环境下资源分配的公平性与系统响应速度之间的微妙平衡。不同于传统摘要的概览性质,本文摘要将直接聚焦于CFS的核心原理、设计目标及面临的挑战,为读者揭开Linux高效调度的秘密。 ####
65 3
|
3月前
|
消息中间件 安全 Linux
深入探索Linux操作系统的内核机制
本文旨在为读者提供一个关于Linux操作系统内核机制的全面解析。通过探讨Linux内核的设计哲学、核心组件、以及其如何高效地管理硬件资源和系统操作,本文揭示了Linux之所以成为众多开发者和组织首选操作系统的原因。不同于常规摘要,此处我们不涉及具体代码或技术细节,而是从宏观的角度审视Linux内核的架构和功能,为对Linux感兴趣的读者提供一个高层次的理解框架。
|
4月前
|
存储 缓存 算法
C语言在实现高效算法方面的特点与优势,包括高效性、灵活性、可移植性和底层访问能力
本文探讨了C语言在实现高效算法方面的特点与优势,包括高效性、灵活性、可移植性和底层访问能力。文章还分析了数据结构的选择与优化、算法设计的优化策略、内存管理和代码优化技巧,并通过实际案例展示了C语言在排序和图遍历算法中的高效实现。
94 2