Android系统启动1————概述和Liunx内核的启动

た 入场券 2022-03-12 08:14 339阅读 0赞

Android系统启动1————概述和Linux内核的启动

文章目录

  • Android系统启动1————概述和Linux内核的启动
      • 一.概述
        • 1.系统启动的整体流程
        • 2.init进程简介
        • 3.Zygote进程简介
        • 4.SystemServer进程简介
      • 二.BootLoader的启动
        • 1.BootLoaderd的启动1-汇编阶段
        • 2.BootLoaderd的启动2-c语言阶段
      • 三.Linux的启动
        • 1.head.S
        • 2.start_kernel
        • 3.kernel进程
      • 四.小结
      • 五.参考资料

一.概述

1.系统启动的整体流程

Android启动大致分为三个阶段

  • BootLoader引导即uBoot.bin
  • linux内核启动即zImage
  • Android系统启动即ramdisk.img与system.img

其中Android启动又可以分为下面的步骤

  • init进程的启动
  • zygote进程的启动
  • system Service进程的启动
  • 最后Launcher启动Home Activity

大致过程如下图所示:
在这里插入图片描述其中1,2,3是linux启动过程,4,5,6是android启动过程。

2.init进程简介

init进程是Android系统中用户空间的第一个进程,作为第一个进程,它被赋予了很多极其重要的工作职责,比如创建zygote(孵化器)和属性服务等。init进程是由多个源文件共同组成的,这些文件位于源码目录system/core/init。

也就是说,init进程负责两件事

  • 负责创建系统中比较关键的进程,比如说zygote进程
  • Andorid系统中有很多属性,于是init进程就提供了一个property Service(属性服务)来管理他们

3.Zygote进程简介

在Android中,虚拟机,应用程序进程以及运行系统的关键服务的SystemServer进程都是由Zygote进程来创建的,我们也将它称为孵化器。它通过fock(复制进程)的形式来创建应用程序进程和SystemServer进程,由于Zygote进程在启动时会创建DVM,因此通过fock而创建的应用程序进程和SystemServer进程可以在内部获取一个DVM的实例拷贝。

也就是说,Zygote进程主要负责下面三件事

  • 启动虚拟机
  • 创建SystemServer进程
  • 创建应用进程

4.SystemServer进程简介

SystemServer的进程名实际上叫做“system_server”,通常简称为SS。

系统中的服务驻留在其中,常见的比如WindowManagerServer(Wms)、ActivityManagerSystemService(AmS)、 PackageManagerServer(PmS)等,这些系统服务都是以一个线程的方式存在于SystemServer进程中。

也就是说,在SystemServer中,主要负责各种服务的创建。

二.BootLoader的启动

当按开机键的时候,引导芯片开始从固化在ROM的预设代码开始执行,然后加载引导程序到RAM。这个引导程序就是BootLoader()。因为笔者没有做过单片机的相关学习,所以下面的内容是我根据网上的资料整理而来。文末会附上链接。

BootLoaderd 分为两个阶段,一个阶段是汇编部分,一个阶段是C语言部分.

另外,关于BootLoader有很多的实现,下面以一个比较通用的BootLoader实现 uBoot为例

1.BootLoaderd的启动1-汇编阶段

目录:cpu/arm920t/start.S

a.设置CPU进入SVC模式(系统管理模式),cpsr[4:0]=0xd3

  1. #include <common.h>
  2. #include <config.h>
  3. //u-boot的主入口,跳入了后面的start_code
  4. .globl _start
  5. //这些是跳转向量表,和芯片的体系结构有关
  6. // ldr语句的意思是将第二个操作数(如:_undefined_instruction)指向的地址数据传给PC
  7. _start: b start_code
  8. ldr pc, _undefined_instruction
  9. ldr pc, _software_interrupt
  10. ldr pc, _prefetch_abort
  11. ldr pc, _data_abort
  12. ldr pc, _not_used
  13. ldr pc, _irq
  14. ldr pc, _fiq
  15. //.word 为定义一个4字节的空间 undefined_instruction 为地址, 即后面标号所对的偏移地址数据
  16. //undefined_instruction 为地址, 即后面标号所对的偏移地址数据
  17. _undefined_instruction: .word undefined_instruction
  18. _software_interrupt: .word software_interrupt
  19. _prefetch_abort: .word prefetch_abort
  20. _data_abort: .word data_abort
  21. _not_used: .word not_used
  22. _irq: .word irq
  23. _fiq: .word fiq
  24. //16字节对齐,并以0xdeadbeef填充,它是个Magic number 。
  25. .balignl 16,0xdeadbeef

b.接着进入Start_code中:设置CPU进入SVC模式。

  1. /*
  2. * the actual start code
  3. */
  4. start_code:
  5. /*
  6. * set the cpu to SVC32 mode
  7. */
  8. mrs r0, cpsr
  9. bic r0, r0, #0x1f
  10. orr r0, r0, #0xd3
  11. msr cpsr, r0
  12. bl coloured_LED_init
  13. bl red_LED_on

c.关看门狗,WTCON = 0x0,并设置寄存器地址

”watchdog”,俗称“看门狗”。“Watchdog” 在实现上可以是硬件电路也可以是软件定时器,能够在系统出现故障时自动重新启动系统。

  1. 复制代码
  2. /* turn off the watchdog */
  3. # if defined(CONFIG_S3C2400)
  4. # define pWTCON 0x15300000
  5. # define INTMSK 0x14400008 /* Interupt-Controller base addresses */
  6. # define CLKDIVN 0x14800014 /* clock divisor register */
  7. #else
  8. # define pWTCON 0x53000000
  9. # define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
  10. # define INTSUBMSK 0x4A00001C
  11. # define CLKDIVN 0x4C000014 /* clock divisor register */
  12. # endif
  13. //关看门狗
  14. ldr r0, =pWTCON
  15. mov r1, #0x0
  16. str r1, [r0]

d.关中断,INTMSK=0xFFFFFFFF, INTSUBMSK=0x3FF。

  1. /*
  2. * mask all IRQs by setting all bits in the INTMR - default
  3. */
  4. mov r1, #0xffffffff
  5. ldr r0, =INTMSK
  6. str r1, [r0]
  7. # if defined(CONFIG_S3C2410)
  8. ldr r1, =0x3ff
  9. ldr r0, =INTSUBMSK
  10. str r1, [r0]
  11. # endif

e.时钟设置CLKDIVN=0x3 , FCLK:HCLK:PCLK = 1:2:4

  1. /* FCLK:HCLK:PCLK = 1:2:4 */
  2. /* default FCLK is 120 MHz ! */
  3. ldr r0, =CLKDIVN
  4. mov r1, #3
  5. str r1, [r0]
  6. #endif /* CONFIG_S3C24X0 */

f.询问是否进行CPU初始化

  1. #ifndef CONFIG_SKIP_LOWLEVEL_INIT
  2. bl cpu_init_crit
  3. #endif

g.初始化堆栈,如果要在C语言环境下执行代码,就必须初始化堆栈

  1. /* Set up the stack */
  2. stack_setup:/* 设置栈指针 */
  3. ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
  4. sub r0, r0, #CONFIG_SYS_MALLOC_LEN /* malloc area */
  5. sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE /* bdinfo */
  6. #ifdef CONFIG_USE_IRQ
  7. sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
  8. #endif
  9. sub sp, r0, #12 /* leave 3 words for abort-stack */
  10. clear_bss:
  11. ldr r0, _bss_start /* find start of bss segment */
  12. ldr r1, _bss_end /* stop here */
  13. mov r2, #0x00000000 /* clear */
  14. clbss_l:str r2, [r0] /* clear loop... */
  15. add r0, r0, #4
  16. cmp r0, r1
  17. ble clbss_l

只要将sp指针指向一段没有被使用的内存就完成栈的设置了。根据上面的代码可以知道U-Boot内存使用情况了,如下图所示:
在这里插入图片描述
h.CPU的初始化,即cpu_init_crit函数,完成以后回到主函数

  1. #ifndef CONFIG_SKIP_LOWLEVEL_INIT
  2. cpu_init_crit:
  3. /*
  4. * flush v4 I/D caches
  5. */
  6. mov r0, #0
  7. mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */
  8. mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
  9. /*
  10. * disable MMU stuff and caches
  11. */
  12. mrc p15, 0, r0, c1, c0, 0
  13. bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)
  14. bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)
  15. orr r0, r0, #0x00000002 @ set bit 2 (A) Align
  16. orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache
  17. mcr p15, 0, r0, c1, c0, 0
  18. /*
  19. * before relocating, we have to setup RAM timing
  20. * because memory timing is board-dependend, you will
  21. * find a lowlevel_init.S in your board directory.
  22. */
  23. mov ip, lr
  24. bl lowlevel_init
  25. mov lr, ip
  26. mov pc, lr
  27. #endif /* CONFIG_SKIP_LOWLEVEL_INIT */

i.清除bss段

  1. clear_bss: /* 清除bss段 */
  2. ldr r0, _bss_start /* r0 = bss段的起始位置 */
  3. ldr r1, _bss_end @ stop here /* r1 = bss段结束位置 */
  4. mov r2, #0x0 @ clear value /* r2 = 0 */
  5. clbss_l:
  6. str r2, [r0] @ clear BSS location /* 先将r2,即0x0,存到地址为r0的内存中去 */
  7. cmp r0, r1 @ are we at the end yet /* 比较r0地址和r1地址,即比较当前地址是否到了bss段的结束位置 */
  8. add r0, r0, #4 @ increment clear index pointer /* 然后r0地址加上4 */
  9. bne clbss_l @ keep clearing till at end /* 如果不等于,那么就跳到clbss_l,即接着这几个步骤,直到地址超过了bss的_end位置,即实现了将整个bss段,都清零。*/

j.从这里跳转到第二阶段C代码中去

  1. ldr pc, _start_armboot
  2. _start_armboot: .word start_armboot

汇编第一阶段的代码主要可以分为以下部分:

  • 设置异常向量表
  • 设置特权管理模式
  • 初始化PLL、DDR、MUX…
  • 关MMU,关CACHE
  • 判断代码在RAM还是FLASH,将FLASH代码复制至RAM中
  • 设置堆栈、清空bss段
  • 跳转至C语言处,进入第二阶段

2.BootLoaderd的启动2-c语言阶段

目录:u-boot-2010.06\arch\arm\lib\board.c

第二阶段主要用到了两个数据结构即 gd_t 和 bd_t,这两个类型变量记录了刚启动时的信息,还将记录作为引导内核和文件系统的参数,如 bootargs 等,并且将来还会在启动内核时,由 uboot 交由 kernel 时会有所用。其定义如下:
gd_t :

  1. #### u-boot-2010.06\arch\arm\include\asm\global_data.h ####
  2. /* U-Boot使用了一个存储在寄存器中的指针gd来记录全局数据区的地址,这个指针存放在指定的寄存器r8中 */
  3. typedef struct global_data {
  4. /* 全局数据结构 */
  5. bd_t *bd; /* 指向板级信息结构 */
  6. unsigned long flags; /* 标记位 */
  7. unsigned long baudrate; /* 串口波特率 */
  8. unsigned long have_console; /* serial_init() was called */
  9. unsigned long env_addr; /* 环境参数地址 */
  10. unsigned long env_valid; /* 环境参数 CRC 校验有效标志 */
  11. unsigned long fb_base; /* fb 起始地址 */
  12. #ifdef CONFIG_VFD
  13. unsigned char vfd_type; /* 显示器类型(VFD代指真空荧光屏) */
  14. #endif
  15. #ifdef CONFIG_FSL_ESDHC /* 宏未定义 */
  16. unsigned long sdhc_clk;
  17. #endif
  18. #if 0 /* 未定义 */
  19. unsigned long cpu_clk; /* cpu 频率*/
  20. unsigned long bus_clk; /* bus 频率 */
  21. phys_size_t ram_size; /* ram 大小 */
  22. unsigned long reset_status; /* reset status register at boot */
  23. #endif
  24. void **jt; /* 跳转函数表 */
  25. } gd_t;

bd_t

  1. typedef struct bd_info {
  2. /* 板级信息结构 */
  3. int bi_baudrate; /* 波特率 */
  4. unsigned long bi_ip_addr; /* IP地址 */
  5. struct environment_s *bi_env; /* 板子的环境变量 */
  6. ulong bi_arch_number; /* 板子的 id */
  7. ulong bi_boot_params; /* 板子的启动参数 */
  8. struct /* RAM 配置 */
  9. {
  10. ulong start;
  11. ulong size;
  12. } bi_dram[CONFIG_NR_DRAM_BANKS];
  13. } bd_t;

a.tart_armboot 首先为全局数据结构和板级信息结构分配内存

  1. gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));
  2. /* compiler optimization barrier needed for GCC >= 3.4 */
  3. __asm__ __volatile__("": : :"memory"); /* 内存屏障,防止编译器优化 */
  4. memset ((void*)gd, 0, sizeof (gd_t)); /* 将指定的内存地址清零( 将全局数据清零 ) */
  5. gd->bd = (bd_t*)((char*)gd - sizeof(bd_t)); /* gd->bd指向一块地址( 取得板级信息数据结构的起始地址 ) */
  6. memset (gd->bd, 0, sizeof (bd_t)); /* gd->db指向地址中的内容清零( 将板级信息清零 ) */
  7. gd->flags |= GD_FLG_RELOC; /* 标记为代码已经转移到 RAM */

b.调用 init_sequence数组中的函数指针完成各部分的初始化

  1. init_fnc_t *init_sequence[] = {
  2. #if defined(CONFIG_ARCH_CPU_INIT)
  3. arch_cpu_init, /* 基本的处理器相关配置 -- basic arch cpu dependent setup */
  4. #endif
  5. timer_init, /* 初始化定时器 -- initialize timer before usb init */
  6. board_init, /* 板级特殊设备初始化(很重要) -- basic board dependent setup */
  7. #if defined(CONFIG_USE_IRQ)
  8. interrupt_init, /* 中断初始化 -- set up exceptions */
  9. #endif
  10. // timer_init, /* 初始化定时器 */
  11. #ifdef CONFIG_FSL_ESDHC
  12. get_clocks,
  13. #endif
  14. env_init, /* 初始化环境变量(默认的环境变量) -- initialize environment */
  15. init_baudrate, /* 初始化波特率设置 -- initialze baudrate settings */
  16. serial_init, /* 初始化串口 */
  17. console_init_f, /* 控制台初始化 -- stage 1 init of console */
  18. display_banner, /* 打印uboot版本信息 -- say that we are here */
  19. #if defined(CONFIG_DISPLAY_CPUINFO)
  20. print_cpuinfo, /* 显示cpu信息 -- display cpu info (and speed) */
  21. #endif
  22. #if defined(CONFIG_DISPLAY_BOARDINFO)
  23. checkboard, /* 显示板级信息 -- display board info */
  24. #endif
  25. #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
  26. init_func_i2c, /* 初始化IIC,hard:真正iic,soft:gpio模拟iic */
  27. #endif
  28. dram_init, /* 配置可用RAM -- configure available RAM banks */
  29. #if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI)
  30. arm_pci_init, /* 初始化pci */
  31. #endif
  32. NULL,
  33. };
  34. /* 函数指针,执行指针数组中的内容(实际内容为函数指针),初始化cpu、总线、设备等等*/
  35. for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
  36. if ((*init_fnc_ptr)() != 0) {
  37. hang ();
  38. }
  39. }
  40. void hang (void) {
  41. puts ("### ERROR ### Please RESET the board ###\n");
  42. for (;;);
  43. }

c.start_armboot 在接下来的流程中还做了如下操作:

  1. void start_armboot (void)
  2. {
  3. nand_init(); /* 初始化 NAND */
  4. mmc_initialize(0); /* 初始化MMC */
  5. mmc_flash_init(0);
  6. env_relocate () /* 重定位环境变量,将其从 NAND 拷贝到内存中 */
  7. gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr"); /* 设置IP地址 */
  8. stdio_init (); /* 初始化外设 */
  9. jumptable_init (); /* 初始化跳转函数表 */
  10. console_init_r (); /* 控制台初始化第二阶段 */
  11. misc_init_r (); /* 杂项设备初始化, eg:battery */
  12. enable_interrupts (); /* 使能中断 */
  13. #ifdef CONFIG_KEDACOM_E2PROM
  14. extern int kd_set_ethaddr();
  15. kd_set_ethaddr();
  16. #endif
  17. /* 如果存在则从环境变量中读取装载地址,其默认为 ulong load_addr = CONFIG_SYS_LOAD_ADDR; */
  18. if ((s = getenv ("loadaddr")) != NULL) {
  19. load_addr = simple_strtoul (s, NULL, 16);
  20. }
  21. #if defined(CONFIG_CMD_NET)
  22. if ((s = getenv ("bootfile")) != NULL) {
  23. copy_filename (BootFile, s, sizeof (BootFile));
  24. }
  25. #endif
  26. #if defined(CONFIG_CMD_NET)
  27. eth_initialize(gd->bd); /* 网络初始化 */
  28. #endif
  29. #if defined(CONFIG_BOOTROM_SUPPORT)
  30. extern void download_boot(const int (*handle)(void));
  31. download_boot(NULL);
  32. #endif
  33. product_control();
  34. #ifdef CONFIG_PARTTAB_ON_FLASH
  35. partition_check_update_flags();
  36. #endif
  37. /* main_loop() can return to retry autoboot, if so just run it again. */
  38. for (;;) {
  39. main_loop (); /* 进入主循环 common/main.c */
  40. }
  41. }

d.main_loop 函数

  1. #### u-boot-2010.06\arch\arm\lib\board.c ####
  2. void main_loop (void)
  3. {
  4. setenv ("ver", version_string); /* 设置版本信息 */
  5. update_tftp ();
  6. ….
  7. #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
  8. s = getenv ("bootdelay"); /* 获取bootdelay环境变量的值 */
  9. bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; /* 将字符串转换为long类型变量 */
  10. debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);
  11. debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
  12. /* 倒数读秒,如果delay时间内没有操作,执行run_command命令 */
  13. if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
  14. run_command (s, 0);
  15. }
  16. #endif /* CONFIG_BOOTDELAY */
  17. for (;;) {
  18. len = readline (CONFIG_SYS_PROMPT); /* 读取输入 */
  19. flag = 0; /* assume no special flags for now */
  20. if (len > 0)
  21. strcpy (lastcommand, console_buffer); /* 将输入保存到历史记录中 */
  22. else if (len == 0)
  23. flag |= CMD_FLAG_REPEAT; /* 如果没有输入则重复上次 */
  24. if (len == -1)
  25. puts ("<INTERRUPT>\n");
  26. else
  27. rc = run_command(lastcommand, flag); /* 执行命令 */
  28. lastcommand[0] = 0; /* 将命令置无效命令令其不可重复 */
  29. }
  30. }

e.通过一个函数指针 thekernel()带三个参数跳转到内核( zImage )入口点开始执行

此时, u-boot 的任务已经完成,控制权完全交给内核( zImage )。在 uBoot 的文件lib_arm\bootm.c中定义了 thekernel, 并在 do_bootm_linux 的最后执行 thekernel。定义thekernel函数指针,获取bootargs参数给commandline指针。theKernel (0, machid, bd->bi_boot_params);第一个参数必须为0,第二个参数为机器类型ID,第三个参数为传递给内核参数的起始地址0x30000100

  1. int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images)
  2. {
  3. bd_t *bd = gd->bd;
  4. char *s;
  5. int machid = bd->bi_arch_number;
  6. void (*theKernel)(int zero, int arch, uint params);
  7. int ret;
  8. #ifdef CONFIG_CMDLINE_TAG
  9. char *commandline = getenv ("bootargs");
  10. #endif
  11. if ((flag != 0) && (flag != BOOTM_STATE_OS_GO))
  12. return 1;
  13. theKernel = (void (*)(int, int, uint))images->ep;
  14. s = getenv ("machid");
  15. if (s) {
  16. machid = simple_strtoul (s, NULL, 16);
  17. printf ("Using machid 0x%x from environment\n", machid);
  18. }
  19. ret = boot_get_ramdisk(argc, argv, images, IH_ARCH_ARM,
  20. &(images->rd_start), &(images->rd_end));
  21. if(ret)
  22. printf("[err] boot_get_ramdisk\n");
  23. show_boot_progress (15);
  24. debug ("## Transferring control to Linux (at address %0 lx) ...\n",
  25. (ulong) theKernel);
  26. #if defined (CONFIG_SETUP_MEMORY_TAGS) || \
  27. defined (CONFIG_CMDLINE_TAG) || \
  28. defined (CONFIG_INITRD_TAG) || \
  29. defined (CONFIG_SERIAL_TAG) || \
  30. defined (CONFIG_REVISION_TAG) || \
  31. defined (CONFIG_LCD) || \
  32. defined (CONFIG_VFD)
  33. setup_start_tag (bd);
  34. #ifdef CONFIG_SERIAL_TAG
  35. setup_serial_tag (&params);
  36. #endif
  37. #ifdef CONFIG_REVISION_TAG
  38. setup_revision_tag (&params);
  39. #endif
  40. #ifdef CONFIG_SETUP_MEMORY_TAGS
  41. setup_memory_tags (bd);
  42. #endif
  43. #ifdef CONFIG_CMDLINE_TAG
  44. setup_commandline_tag (bd, commandline);
  45. #endif
  46. #ifdef CONFIG_INITRD_TAG
  47. if (images->rd_start && images->rd_end)
  48. setup_initrd_tag (bd, images->rd_start, images->rd_end);
  49. #endif
  50. #if defined (CONFIG_VFD) || defined (CONFIG_LCD)
  51. setup_videolfb_tag ((gd_t *) gd);
  52. #endif
  53. setup_end_tag (bd);
  54. #endif
  55. /* we assume that the kernel is in place */
  56. printf ("\nStarting kernel ...\n\n");
  57. #ifdef CONFIG_USB_DEVICE
  58. {
  59. extern void udc_disconnect (void);
  60. udc_disconnect ();
  61. }
  62. #endif
  63. cleanup_before_linux ();
  64. theKernel (0, machid, bd->bi_boot_params);
  65. /* does not return */
  66. return 1;
  67. }

小结,第二阶段代码可以分为下面部分
为gd、bd数据结构分配地址,并清零

  • 执行 init_fnc_ptr 函数指针数组中的各个初始化函数:板级特殊设备初始化(board_init)、时钟初始化(timer_init)、初始化环境变量(env_init)、串口控制台初始化(init_baudrate、console_init_f)、打印U-Boot信息(display_banner、print_cpuinfo、checkboard)、配置可用RAM大小(dram_init)
  • 对gd, bd 数据结构赋值初始化
  • 各种设备初始化
  • NAND Flash初始化 (nand_init) 、MMC初始化 (mmc_initialize、mmc_flash_init) 、网络初始化 (eth_initialize)、初始化串口(serial_init、console_init_r) 、初始化其他外设(stdio_init)、杂项设备初始化(misc_init_r)
  • 环境变量代码重定位(env_relocate)
  • 使能中断(enable_interrupts)
  • 进入主循环(main_loop)
  • 调用 thekernel进入kernel,启动Linux系统

三.Linux的启动

关于Linux系统启动主要分为三个阶段,第一个阶段是自解压过程,第二个是设置ARM处理器的工作模式、设置一级页表等,第三个阶段主要是C代码,包括Android的初始化的全部工作。

  • 自解压过程,内核压缩和解压缩目录kernel/arch/boot/compressed,编译完成后将产生head.o、misc.o、piggy.gzip.o、vmlinux、decompress.o这几个文件。
  • 解压缩完成后就进入Kernel初始化阶段。也就是我着重介绍的部分。
  • 第三阶段主要是C代码,包括Android的初始化的全部工作

1.head.S

  1. * Non-board-specific low-level startup code
  2. *
  3. * Copyright (C) 2004-2006 Atmel Corporation
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License version 2 as
  7. * published by the Free Software Foundation.
  8. */
  9. #include <linux/linkage.h>
  10. #include <asm/page.h>
  11. .section .init.text,"ax"
  12. .global kernel_entry
  13. kernel_entry:
  14. /* Start the show */
  15. lddpc pc, kernel_start_addr
  16. .align 2
  17. kernel_start_addr:
  18. .long start_kernel

从上面的代码我们看出,最终调用了start_kernel方法

2.start_kernel

  1. asmlinkage __visible void __init start_kernel(void)
  2. {
  3. char *command_line;
  4. char *after_dashes;
  5. /*
  6. * Need to run as early as possible, to initialize the lockdep hash:
  7. * 需要尽早运行,初始化lockdep散列:
  8. */
  9. lockdep_init();
  10. set_task_stack_end_magic(&init_task);
  11. smp_setup_processor_id();
  12. debug_objects_early_init();
  13. /*
  14. * Set up the the initial canary ASAP:
  15. * 尽快设置初始ASAP:
  16. */
  17. boot_init_stack_canary();
  18. cgroup_init_early();
  19. local_irq_disable();
  20. early_boot_irqs_disabled = true;
  21. /*
  22. * Interrupts are still disabled. Do necessary setups, then
  23. * enable them
  24. */
  25. boot_cpu_init();
  26. page_address_init();
  27. pr_notice("%s", linux_banner);
  28. setup_arch(&command_line);
  29. mm_init_cpumask(&init_mm);
  30. setup_command_line(command_line);
  31. setup_nr_cpu_ids();
  32. setup_per_cpu_areas();
  33. smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
  34. build_all_zonelists(NULL, NULL);
  35. page_alloc_init();
  36. pr_notice("Kernel command line: %s\n", boot_command_line);
  37. parse_early_param();
  38. after_dashes = parse_args("Booting kernel",
  39. static_command_line, __start___param,
  40. __stop___param - __start___param,
  41. -1, -1, NULL, &unknown_bootoption);
  42. if (!IS_ERR_OR_NULL(after_dashes))
  43. parse_args("Setting init args", after_dashes, NULL, 0, -1, -1, NULL, set_init_arg);
  44. jump_label_init();
  45. /*
  46. * These use large bootmem allocations and must precede kmem_cache_init()
  47. */
  48. setup_log_buf(0);
  49. pidhash_init();
  50. vfs_caches_init_early();
  51. sort_main_extable();
  52. trap_init();
  53. mm_init();
  54. /*
  55. * Set up the scheduler prior starting any interrupts (such as the
  56. * timer interrupt). Full topology setup happens at smp_init()
  57. * time - but meanwhile we still have a functioning scheduler.
  58. */
  59. //初始化每个处理器的可运行队列,设置系统初始化进程即0号进程
  60. sched_init();
  61. /*
  62. * Disable preemption - early bootup scheduling is extremely
  63. * fragile until we cpu_idle() for the first time.
  64. */
  65. preempt_disable();
  66. if (WARN(!irqs_disabled(), "Interrupts were enabled *very* early, fixing it\n"))
  67. local_irq_disable();
  68. idr_init_cache();
  69. rcu_init();
  70. /* trace_printk() and trace points may be used after this */
  71. trace_init();
  72. context_tracking_init();
  73. radix_tree_init();
  74. /* init some links before init_ISA_irqs() */
  75. early_irq_init();
  76. init_IRQ();
  77. tick_init();
  78. rcu_init_nohz();
  79. init_timers();
  80. hrtimers_init();
  81. softirq_init();
  82. timekeeping_init();
  83. time_init();
  84. sched_clock_postinit();
  85. perf_event_init();
  86. profile_init();
  87. call_function_init();
  88. WARN(!irqs_disabled(), "Interrupts were enabled early\n");
  89. early_boot_irqs_disabled = false;
  90. local_irq_enable();
  91. kmem_cache_init_late();
  92. /*
  93. * HACK ALERT! This is early. We're enabling the console before
  94. * we've done PCI setups etc, and console_init() must be aware of
  95. * this. But we do want output early, in case something goes wrong.
  96. */
  97. console_init();
  98. if (panic_later)
  99. panic("Too many boot %s vars at `%s'", panic_later, panic_param);
  100. lockdep_info();
  101. /*
  102. * Need to run this when irqs are enabled, because it wants
  103. * to self-test [hard/soft]-irqs on/off lock inversion bugs
  104. * too:
  105. */
  106. locking_selftest();
  107. #ifdef CONFIG_BLK_DEV_INITRD
  108. if (initrd_start && !initrd_below_start_ok &&
  109. page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {
  110. pr_crit("initrd overwritten (0x%08lx < 0x%08lx) - disabling it.\n",
  111. page_to_pfn(virt_to_page((void *)initrd_start)), min_low_pfn);
  112. initrd_start = 0;
  113. }
  114. #endif
  115. page_ext_init();
  116. debug_objects_mem_init();
  117. kmemleak_init();
  118. setup_per_cpu_pageset();
  119. numa_policy_init();
  120. if (late_time_init)
  121. late_time_init();
  122. sched_clock_init();
  123. calibrate_delay();
  124. pidmap_init();
  125. anon_vma_init();
  126. acpi_early_init();
  127. #ifdef CONFIG_X86
  128. if (efi_enabled(EFI_RUNTIME_SERVICES))
  129. efi_enter_virtual_mode();
  130. #endif
  131. #ifdef CONFIG_X86_ESPFIX64
  132. /* Should be run before the first non-init thread is created */
  133. init_espfix_bsp();
  134. #endif
  135. thread_stack_cache_init();
  136. cred_init();
  137. fork_init();
  138. proc_caches_init();
  139. buffer_init();
  140. key_init();
  141. security_init();
  142. dbg_late_init();
  143. vfs_caches_init();
  144. signals_init();
  145. /* rootfs populating might need page-writeback */
  146. page_writeback_init();
  147. proc_root_init();
  148. nsfs_init();
  149. cpuset_init();
  150. cgroup_init();
  151. taskstats_init_early();
  152. delayacct_init();
  153. check_bugs();
  154. acpi_subsystem_init();
  155. sfi_init_late();
  156. if (efi_enabled(EFI_RUNTIME_SERVICES)) {
  157. efi_late_init();
  158. efi_free_boot_services();
  159. }
  160. ftrace_init();
  161. /* Do the rest non-__init'ed, we're now alive */
  162. //调用kernel_thread()创建1号内核线程
  163. rest_init();
  164. }

start_kernel()函数中执行了大量的初始化操作:

  • setup_arch():主要做一些板级初始化,cpu初始化,tag参数解析,u-boot传递的cmdline解析,建立mmu工作页表,初始化内存布局,调用mmap_io建立GPIO、IRQ、MEMCTRL、UART,及其他外设的静态映射表,对时钟,定时器,uart进行初始化
  • sched_init():初始化每个处理器的可运行队列,设置系统初始化进程即0号进程
  • softirq_init():内核的软中断机制初始化函数
  • console_init():初始化系统的控制台结构
  • rest_init():调用kernel_thread()创建1号内核线程,调用schedule()函数切换当前进程,在调用该函数之前,Linux系统中只有两个进程,即0号进程init_task和1号进程kernel_init,其中kernel_init进程也是刚刚被创建的。调用该函数后,1号进程kernel_init将会运行

3.kernel进程

Linux下有三个特殊的进程,idle(swapper)进程(PID = 0),init进程(PID = 1)和看threadd(PID = 2)

  • idle(swapper)进程由系统自动创建,运行在内核态.idle进程其pid=0,其前身是系统创建的第一个进程,也是唯一一个没有通过fork或者kernel_thread产生的进程。

    • 完成加载系统后,演变为进程调度、交换,常常被称为交换进程。
  • init进程由idle通过kernel_thread创建,在内核空间完成初始化后,加载init进程,并最终转变为用户空间的init进程。

    • 由0进程创建,完成系统的初始化,是系统中所有其他用户进程的祖先进程。
    • linux中的所有进程都是由init进程创建并运行的,首先Linux内核启动,然后在用户空间中启动init进程,在启动其他系统进程
  • Kthreadd进程是idle通过kernel_thread创建,并始终运行在内核空间 ,负责所有内核线程的调度和管理

    • Kthreadd的任务是管理和调度其他内核线程Kernel_thread ,会循环执行一个kthreadd的函数,作用是运行kthreadd_create_list全局链表中维护的kthread
    • 当调用用kernel_thread创建的内核线程会被加入到此链表中,因此所有的内核线程都是直接或者间接的以kthreadd为父进程。

四.小结

在这里插入图片描述前面的文章主要分析了2,3过程。
即BootLoader和Linux内核的启动过程。
下一篇介绍Android初始化语言。
之后继续分析init ,zygote,SystemService进程

五.参考资料

《Android高级进阶》
《深入理解android 卷一》
U-Boot – uboot代码深度解析
Android启动流程简析

发表评论

表情:
评论列表 (有 0 条评论,339人围观)

还没有评论,来说两句吧...

相关阅读