=====================================================
arm linux系统启动相关文章列表:
arm linux系统启动流程 http://blog.csdn.net/u010872301/article/details/72615117
分析arm linux启动打印信息 http://blog.csdn.net/u010872301/article/details/78779503
Linux kernel panic 问题解决方案 http://blog.csdn.net/u010872301/article/details/73928935
=====================================================
面试中经常遇到此类问题arm linux系统启动流程,首先我们遇到此类问题必须明白题目所要考察我们的目的是什么:
1:考察点:在嵌入式开发中,遇到linux或Android系统启动报错时确定问题的位置,快速定位并解决问题。
2:问题所涉及到的知识点有:Bootloader、Linux和Android的系统启动流程。
3:解答思路:
首先需要我们清楚整体的嵌入式移植开发流程图:
U-Boot启动内核的过程可以分为两个阶段,两个阶段的功能如下:
(1)第一阶段主要包含依赖于CPU的体系结构硬件初始化的代码,通常都用汇编语言来实现。
硬件设备初始化(屏蔽所有的中断、设置CPU的速度和时钟频率、RAM初始化、初始化LED、关闭CPU内部指令/数据Cache等)
把Bootloader 为第二阶段准备RAM空间。
复制Bootloader的第二阶段代码到RAM空间中。
设置堆栈
跳转到第二阶段的C程序入口点
(2)第二阶段的功能
初始化本阶段使用的硬件设备
检测系统内存映射
将内核映像和根文件系统映像从Flash读到RAM
为内核设置启动参数
调用内核
代码如下:
第一阶段:主要是汇编代码
①进入arch/arm/cpu/armv7/start.S 直接在物理地址执行(uImage中)
⑴建立中断异常向量表
39 _start: b reset
||
\/
127 bl save_boot_params 保存了当前cpu的运行状态,reset则进行跳转
⑵设置svc模式
131 mrs r0, cpsr
132 bic r0, r0, #0x1f
133 orr r0, r0, #0xd3
134 msr cpsr,r0
设置向量,为设置协处理器做准备
167 bl cpu_init_cp15 完成了我们对协处理器设置,⑶关闭了MMU和cache
||
\/
168 bl cpu_init_crit
||
\/
b lowlevel_init(②board/samsung/fs4412/lowlevel_init.S )
设置了reset,使能reset
⑷关闭了开门狗(不需要帮我们重启,为了观察现象)
⑸ 初始化时钟,⑹初始化内存,进行判断uboot是否运行在物理地址中TEXT_BASE (0x43e000000)
进行⑺串口初始化操作,我们已经可以使用串口显示
push {lr} ..... pop {pc} ==> mov pc,lr
bl _main(③arch/arm/lib/crt0.S )
⑻初始化堆栈 ,准备启动C语言
为gd结构体的赋值做准备(提供gd结构体大小的空间)
||
\/
115 bl board_init_f (④arch/arm/lib/board.c )
⑼ 给gd结构体进行赋值 gd结构体定义(⑤include/asm/global_data.h )
303 for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
304 if ((*init_fnc_ptr)() != 0) {
305 hang ();
306 }
307 }
完成各种板子初始化操作,最重要的是初始化了DRAM控制器,我开始使用400000000-800000000物理地址
第二阶段:主要是c代码
回到arch/arm/lib/crt0.S
进行寄存器赋值
r8 = gd->start_addr_sp
lr = here
r0 = gd->start_addr_sp u-boot起始地址
r2 = gd->relocaddr 搬移地址
||
\/
b relocate_code(⑥arch/arm/cpu/armv7/start.S)
196 copy_loop:
197 ldmia r0!, {r9-r10} /* copy from source address [r0] */ 进行循环搬移
198 stmia r1!, {r9-r10} /* copy to target address [r1] */
199 cmp r0, r2 /* until source end address [r2] */
200 blo copy_loop 条件跳转 成立条件是cmp不相等
⑽自搬移关键代码
243 bx lr(链接寄存器) 跳转
||
\/
arch/arm/lib/crt0.S
⑾ 清除bss段 从bss_start到bss_end (物理地址在System.map中) 目的:腾出内存空间
167 ldr pc, =board_init_r
||
\/
arch/arm/lib/board.c
⑿进行最后的初始化操作,开始执行uboot引导系统⒀
702 for (;;) {
703 main_loop();
704 }
||
\/
⑦ common/main.c
获取用户设置的bootcmd等参数,执行uboot
第一阶段:内核的重定位和内核的自解压
第二阶段:执行没有压缩的内核的汇编代码部分
获取CPU信息
检查平台设备号
创建页表
打开MMU
清除BBS段
执行内核C语言部分入口函数
第三阶段:
获取uboot给内核传递的参数
控制台初始化
执行init命令
挂载文件系统
执行用户控件的第一个程序
代码如下:
①进入arch/arm/kernel/head.S
使能thumb指令集,我们可以使用thumb指令,启动了异常处理机制
92 safe_svcmode_maskall r9 使能svc模式
365 mrc p15, 0, r9, c0, c0 @ get processor id
95 bl __lookup_processor_type
||
\/
r4 = 178行虚拟地址 r5 = begin r6 = end
②arch/arm/kernel/head-common.S 进行物理地址和虚拟地址转换,判断处理器类型
148 * r3, r4, r6 corrupted
149 * r5 = proc_info pointer in physical address space
50 * r9 = cpuid (preserved)
118 * r1 = machine no, r2 = atags or dtb,
119 * r8 = phys_offset, r9 = cpuid, r10 = procinfo
121 bl __vet_atags
||
\/
arch/arm/kernel/head-common.S 完成了对设备传参方式的验证(46-50设备树传参)
123 bl __fixup_smp
124 #endif
125 #ifdef CONFIG_ARM_PATCH_PHYS_VIRT
126 bl __fixup_pv_table
127 #endif
进行处理器信息保存,为创建页表做准备
128 bl __create_page_tables 创建页表(创建在物理地址)
arch/arm/mm/proc-v7.S进行armv7处理器的设置
1、开启了cache,tlbs
2、开启clk
3、设置了reset
414 * r0 = cp#15 control register
415 * r1 = machine ID
416 * r2 = atags or dtb pointer
417 * r4 = page table (see ARCH_PGD_SHIFT in asm/memory.h)
418 * r9 = processor ID
419 * r13 = *virtual* address to jump to upon completion
使能mmu需要将页表的物理地址位置指向虚拟地址,我们保存在协处理器中
444 b __turn_mmu_on 完成开启mmu操作
使能成功mmu
进行地址转换前需要进行mmu使能
137 ldr r13, =__mmap_switched @ address to jump to after
138 @ mmu has been enabled
||
\/
81 adr r3, __mmap_switched_data(类似于这样的adr操作,都是从处理器或者uImage获取到的)
104 b start_kernel
||
\/
③init/main.c 进行各种初始化操作
setuparch()保存了uboot传递的参数,保存在machine(arch/arm/include/asm/mach/arch.h )
652 rest_init();
382 kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
840 kernel_init_freeable();
928 prepare_namespace();
||
\/
④ init/do_mounts.c
589 mount_root(); 进行文件系统判断
mount_nfs_root()完成了对文件系统的挂载
内核启动:head.S head-common.S init/main.c init/do_mount.c
Init进程是Linux内核启动后创建的第一个用户进程,地位非常重要,Init进程在初始化过程中会启动很多重要的守护进程,因此,了解Init进程的启动过程将有助于我们更好的理解Android系统。Init除了完成系统的初始化之外,本身也是一个守护进程,负担着系统部分很重要的职责。
知识点:介绍Init进程的初始化以及它作为守护进程的功能。
在介绍Init进程前,我们先简单介绍Android的启动过程。从系统角度看,Android的启动过程可分为bootloader引导,装载和启动Linux内核,启动Android系统,3个大的阶段。其中Android系统的启动还可以细分为启动Init进程,启动zygote,启动SystemService,启动serviceManager,启动Home等多个阶段
1)bootloader引导
当我们按下手机的电源键,最先运行的就是bootloader.bootloader主要的作用是初始化基本的硬件设备(如CPU,内存,Flash等)并且通过建立内存空间映射,为装载Linux内核准备好合适的运行环境.一旦Linux内核装载完毕,bootloader将会从内存中清除掉.
如果用户在Bootloader运行期间,按下预定义的组合键,可以进入系统的更新模块.Android的下载可以选择进入FastBoot模式和Recovery模式.
Fastboot是Android设计的一套通过USB来更新手机分区映像的协议(绕写分区镜像),方便开发人员能快速更新制定的手机分区.但是一般的零售机往往去掉了Fastboot,Google销售的开发机则带有Fastboot模块.
Recovery模式是Android 特有的升级系统.利用Recovery模式,手机可以进行回复出厂设置,或者执行OTA,补丁和固件升级.进入Recovery模式实际上是启动了一个文本模式的Linux.
2) 装载和启动Linux内核.
Android的boot.img存放的就是Linux内核(system.img)和一个根文件系统(ramdisk.img).Bootloader会把boot.img映像装载进内存.然后Linux内核会执行整个系统的初始化,完成后装载根文件系统,最后启动Init进程.
3) 启动Init进程.
Linux内核加载完毕后,会首先启动Init进程,Init进程是系统的第一个进程.在Init进程的启动过程中,会解析Linux的配置脚本init.rc文件(脚本规则).根据init.rc文件的内容,init进程会装载Android的文件系统,创建系统目录(adb shell 系统目录),初始化属性系统(进程全局变量),启动Android系统重要的守护进程(后台进程),这些进程包括USB守护进程,adb守护进程(debug桥),vold守护进程(外部存储,检测热插拔sd卡(EXT4文件系统),检测挂载),rild守护进程(电话卡)等.
最后Init进程也会作为守护进程来执行修改属性请求,重启崩溃的进程等操作.
4) 启动serviceManager((本地层)进程)和四大组件没有关系
ServiceManager由Init进程启动.它主要的作用是管理Binder服务,负责Binder服务的注册与查找.
5)启动zygote进程.(受精卵)--------》(之后是java进程)
Init进程初始化结束时,会启动zygote进程.zygote进程负责fork出应用进程(复制整个父进程数据),是所有应用进程的父进程.zygote进程初始化时会创建Dalivik虚拟机(运行java程序),预装载系统的资源文件(Button,farmwork和库资源)和Java类.所有从Zygote进程fork出的用户进程(app应用程序)将继承和共享这些预加载的资源,不用浪费时间重新加载,加快了应用程序的启动过程.启动结束后,zygote进程也将变为守护进程,负责响应启动APK应用程序的请求(提高运行效率,预加载)
6) 启动SystemServer.
SystemServer是zygote进程fork出的第一个进程,也是整个Android系统的核心进程.在SystemServer中运行着Android系统大部分的Binder服务.SystemServer首先启动本地服务SensorService(传感器);接着启动包括 ActivityManagerService (四大组件之activity底层实现),WindowsManagerService(APP窗口),PackageManagerService(包管理)在内的所有Java服务. (接口API通过Launcher调用)
7)启动MediaServer(摄像头,音视、频服务)
MediaServer由Init进程启动.它包含了一些多媒体相关的本地BInder服务,包括:cameraService,AudioFlingerService,MediaPlayerService和AudioPolicyService.
8) 启动Launcher(桌面)(java代码写的)
SystemServer加载完所有Java服务后,最后会调用ActivityManagerService的SystemReady()方法.在这个方法的执行中,会发出Intent "android.intent.category.HOME".凡是相应这个Intent的apk应用都会运行起来,Launcher应用是Android系统默认的桌面应用,一般只有它会相应这个Intent,因此,系统开机后,第一个运行的应用就是Launcher.