arm linux系统启动流程

来源:互联网 发布:ios手游源码 编辑:程序博客网 时间:2024/06/05 00:31

面试中经常遇到此类问题arm linux系统启动流程,首先我们遇到此类问题必须明白题目所要考察我们的目的是什么:

1:考察点:在嵌入式开发中,遇到linux或Android系统启动报错时确定问题的位置,快速定位并解决问题。

2:问题所涉及到的知识点有:Bootloader、Linux和Android的系统启动流程

3:解答思路:

首先需要我们清楚整体的嵌入式移植开发流程图:


一、Boot启动流程

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, cpsr132     bic r0, r0, #0x1f133     orr r0, r0, #0xd3134     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



二、arm linux内核启动流程

第一阶段:内核的重定位和内核的自解压

第二阶段:执行没有压缩的内核的汇编代码部分

获取CPU信息

检查平台设备号

创建页表

打开MMU

清除BBS段

执行内核C语言部分入口函数

第三阶段:

获取uboot给内核传递的参数

控制台初始化

执行init命令

挂载文件系统

执行用户控件的第一个程序


设备传参方式
1、2.6内核之前 dev_param结构体进行传参
2、2.6内核后,使用dev_tags 结构体传参
3、3.0后,使用设备树进行传参


代码如下:

①进入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_smp124 #endif125 #ifdef CONFIG_ARM_PATCH_PHYS_VIRT126     bl  __fixup_pv_table                                                                                               127 #endif进行处理器信息保存,为创建页表做准备128     bl  __create_page_tables  创建页表(创建在物理地址)arch/arm/mm/proc-v7.S进行armv7处理器的设置1、开启了cache,tlbs2、开启clk3、设置了reset414  *  r0  = cp#15 control register415  *  r1  = machine ID416  *  r2  = atags or dtb pointer417  *  r4  = page table (see ARCH_PGD_SHIFT in asm/memory.h)418  *  r9  = processor ID419  *  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 after138                         @ 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


三、Android启动流程:

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.


完~





原创粉丝点击