【Android系统启动】 Kernel启动流程

Android驱动框架整理之KERNEL框架 这些带有build的相关目录我没有过研究,有兴趣的可以自己去看看。我们比较关注的是arch/目录下的arm64的源码: 相关的dts目录: 可以在对应的目录进行dts的添加编译或者删除编译 一般android指定对应的dts文件在device目录下进行指定: 如何查看在自己编译的dts内容,每个dts编译完成后都会生成对应的dtb文件,同时还会生成对应的tmp的一个隐藏文件,在对应的out目录下(nxp的目录如下): 可以看到我们指定的dts会有这个隐藏文件产生,然后我们可以进去查看,这个tmp文件会把所有 阅读详情
AI摘要

本文基于AOSP官方源码,对Android Kernel启动流程进行解读。介绍了Android会在Linux Kernel基础上补充形成ACK,以start_kernel为起点,详细解析了从start_kernel到启动Init进程中多个关键函数的功能,如环境初始化、线程创建、执行初始化命令等。

必要说明

  1. Android会在Linux Kenerl基础上做一些Android相关的补充,成为Android Common Kernel(ACK),Android Kernel相关的资料,如Android支持Kernel版本、Kernel发版等信息,可以通过官网内核概览 | Android 开源项目 | Android Open Source Project (google.cn)进行查询;
  2. 文章Android Kernel基于AOSP官方源码common-android14-6.1-lts进行解读,通过查阅资料得知kernel镜像加载后,在C的入口会通过start_kernel进入,因此将此作为起点;

时序图

Main strat_kernel arch_call_rest_init rest_init kernel_init try_to_run_init_process run_init_process kernel_execve Main

源码解析

common/init/main.c - start_kernel()

//  asmlinkage: 这个修饰符通知编译器函数的参数通过堆栈传递,而不是通过寄存器。在许多体系结构中,默认情况下函数参数通过寄存器传递,但是内核函数通常使用堆栈传递参数,因此使用 asmlinkage 修饰符来明确指示这一点。
// __visible: 这个修饰符通知编译器将函数导出为全局符号,以便其他模块可以访问它。在内核中,一些函数需要被其他模块访问,因此使用 __visible 修饰符来明确指示这一点。
// __init: 这个修饰符通知编译器函数在初始化阶段使用,并且在初始化完成后不再需要。在内核中,有些函数只在启动时期间用到,一旦初始化完成就可以释放它们占用的内存,因此使用 __init 修饰符来明确指示这一点。
// __no_sanitize_address: 这个修饰符通知编译器在进行地址检查时不对该函数进行检查。在内核中,由于一些特殊情况和性能要求,可能需要关闭地址检查,因此使用 __no_sanitize_address 修饰符来明确指示这一点。
asmlinkage __visible void __init __no_sanitize_address start_kernel(void)
{
    char *command_line;
    char *after_dashes;

    set_task_stack_end_magic(&init_task);  // 设置任务栈结束标志
    smp_setup_processor_id();  // 设置处理器ID
    debug_objects_early_init();  // 初始化调试对象
    init_vmlinux_build_id();  // 初始化内核的版本信息

    cgroup_init_early();  // 提前初始化控制组

    local_irq_disable();  // 禁用本地中断
    early_boot_irqs_disabled = true;  // 提前禁用引导期间的中断

    /*
     * 中断仍然被禁用。做必要的设置,然后启用中断。
     */
    boot_cpu_init();  // 初始化启动CPU
    page_address_init();  // 初始化页地址
    pr_notice("%s", linux_banner);  // 打印内核版本信息
    early_security_init();  // 提前初始化安全功能
    setup_arch(&command_line);  // 设置体系结构相关参数
    setup_boot_config();  // 设置引导配置
    setup_command_line(command_line);  // 设置内核命令行
    setup_nr_cpu_ids();  // 设置CPU数量
    setup_per_cpu_areas();  // 设置每个CPU的区域
    smp_prepare_boot_cpu();  // 处理启动CPU的特定功能
    boot_cpu_hotplug_init();  // 启动CPU热插拔初始化

    // 更多初始化步骤...
}
这段代码主要负责初始化内核,并启动操作系统的核心功能。
    build_all_zonelists(NULL);  // 构建所有内存区域列表
    page_alloc_init();  // 初始化页面分配器

    pr_notice("Kernel command line: %s\n", saved_command_line);  // 打印内核命令行
    /* 参数可能设置静态键 */
    jump_label_init();  // 初始化跳转标签
    parse_early_param();  // 解析早期参数
    after_dashes = parse_args("Booting kernel",
                              static_command_line, __start___param,
                              __stop___param - __start___param,
                              -1, -1, NULL, &unknown_bootoption);
    print_unknown_bootoptions();  // 打印未知的引导选项
    if (!IS_ERR_OR_NULL(after_dashes))
        parse_args("Setting init args", after_dashes, NULL, 0, -1, -1,
                   NULL, set_init_arg);
    if (extra_init_args)
        parse_args("Setting extra init args", extra_init_args,
                   NULL, 0, -1, -1, NULL, set_init_arg);

    /* 架构和非时间跟踪 rng 初始化,在分配器初始化之前 */
    random_init_early(command_line);  // 提前初始化随机数生成器

    // 更多初始化步骤...

    /*
     * 这些使用大的 bootmem 分配,并且必须在
     * kmem_cache_init() 之前完成
     */
    setup_log_buf(0);  // 设置日志缓冲区
    vfs_caches_init_early();  // 提前初始化VFS缓存
    sort_main_extable();  // 排序主异常表
    trap_init();  // 初始化陷阱
    mm_init();  // 初始化内存管理
    poking_init();  // 初始化POK功能
    ftrace_init();  // 初始化ftrace

    /* trace_printk 可以在这里启用 */
    early_trace_init();  // 提前初始化跟踪功能

    /*
     * 在启动任何中断(如定时器中断)之前设置调度器。
     * 完整的拓扑设置发生在 smp_init() 时 -
     * 但是在此期间我们仍然有一个功能齐全的调度器。
     */
    sched_init();  // 初始化调度器

    if (WARN(!irqs_disabled(),
             "Interrupts were enabled *very* early, fixing it\n"))
        local_irq_disable();  // 如果在非常早期启用了中断,请修复它

    radix_tree_init();  // 初始化基数树
    maple_tree_init();  // 初始化枫树

    // 更多初始化步骤...

    /* 假设在此后 kmem_cache_init() 之后进行 */
    console_init();  // 初始化控制台
    if (panic_later)
        panic("Too many boot %s vars at `%s'", panic_later,
              panic_param);  // 如果出现过多的启动变量,引发内核恐慌

    lockdep_init();  // 初始化锁依赖

    /*
     * 需要在启用中断时运行此操作,因为它想要自我测试
     * [硬件/软件]-irqs on/off锁反转错误
     */
    locking_selftest();  // 锁定自测

#ifdef CONFIG_BLK_DEV_INITRD
    if (initrd_start && !initrd_below_start_ok &&
        page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {
        pr_crit("initrd overwritten (0x%08lx < 0x%08lx) - disabling it.\n",
            page_to_pfn(virt_to_page((void *)initrd_start)),
            min_low_pfn);
        initrd_start = 0;
    }
#endif
    setup_per_cpu_pageset();  // 设置每个CPU的页面集
    numa_policy_init();  // 初始化NUMA策略
    acpi_early_init();  // 提前初始化ACPI
    if (late_time_init)
        late_time_init();  // 如果存在 late_time_init,则进行初始化
    sched_clock_init();  // 初始化调度时钟
    calibrate_delay();  // 校准延迟

	arch_cpu_finalize_init();  // CPU架构初始化的最终化

    pid_idr_init();  // 初始化进程ID分配器
    anon_vma_init();  // 初始化匿名VMA
#ifdef CONFIG_X86
    if (efi_enabled(EFI_RUNTIME_SERVICES))
        efi_enter_virtual_mode();  // 如果启用了EFI运行时服务,进入EFI虚拟模式
#endif
    thread_stack_cache_init();  // 初始化线程堆栈缓存
    cred_init();  // 初始化凭据
    fork_init();  // 初始化fork功能
    proc_caches_init();  // 初始化proc缓存
    uts_ns_init();  // 初始化UTS命名空间
    key_init();  // 初始化密钥
    security_init();  // 初始化安全功能
    dbg_late_init();  // 后期调试功能的初始化
    net_ns_init();  // 初始化网络命名空间
    vfs_caches_init();  // 初始化VFS缓存
    pagecache_init();  // 初始化页面缓存
    signals_init();  // 初始化信号
    seq_file_init();  // 初始化序列文件
    proc_root_init();  // 初始化proc根
    nsfs_init();  // 初始化nsfs
    cpuset_init();  // 初始化cpuset
    cgroup_init();  // 初始化控制组
    taskstats_init_early();  // 提前初始化任务统计

    acpi_subsystem_init();  // 初始化ACPI子系统
    arch_post_acpi_subsys_init();  // ACPI子系统初始化后操作的架构相关初始化
    kcsan_init();  // KCSAN(内核代码静态分析器)的初始化

    /* 执行余下的init函数,现在内核已经活动了 */
    arch_call_rest_init();  // 调用剩余的init函数

    prevent_tail_call_optimization();  // 防止尾调用优化
}

可以看到kernel启动的时候,进行了许多环境相关的初始化,如引导CPU、设置缓存、安全信息、内存管理、proc等虚拟文件系统初始化等等;最后再arch_call_rest_init调用其他的init程序;

common/init/main.c - arch_call_rest_init()

// __init 表示该函数在初始化阶段调用,并且初始化完成后不再需要。
// __weak 表示如果有另一个同名函数被定义为强符号(非弱符号),则使用强符号函数,否则使用当前的弱符号函数。
void __init __weak arch_call_rest_init(void)
{
	rest_init();
}

arch_call_rest_init方法只是对rest_init进行了调用。

common/init/main.c - rest_init()

// 初始化系统的其余部分
noinline void __ref rest_init(void)
{
    struct task_struct *tsk;
    int pid;

    /*
     * 需要先生成init进程,以便它获得pid 1,但是init任务最终会想要创建k线程,
     * 如果我们在创建kthreadd之前安排它,就会发生OOPS。
     */
    // 在用户模式下创建一个线程,执行kernel_init函数
    pid = user_mode_thread(kernel_init, NULL, CLONE_FS);
    /*
     * 将init固定在引导CPU上。在运行sched_init_smp()之前,任务迁移不起作用。
     * 它将为init设置允许的CPU为非隔离CPU。
     */
    rcu_read_lock();
    tsk = find_task_by_pid_ns(pid, &init_pid_ns);
    tsk->flags |= PF_NO_SETAFFINITY;
    set_cpus_allowed_ptr(tsk, cpumask_of(smp_processor_id()));
    rcu_read_unlock();

    // ... 此处省略
}

在rest_init()函数中,在用户模式下创建一个线程,用于执行kernel_init函数,以确保初始化进程获得pid 1。然后将init固定在引导CPU上,并设置允许的CPU为非隔离CPU。

common/init/main.c - kernel_init()

static int __ref kernel_init(void *unused)
{
	int ret;

	/*
	 * 等待kthreadd线程设置完成。
	 */
	wait_for_completion(&kthreadd_done);

	// 进行内核初始化的可释放部分
	kernel_init_freeable();
	/* 需要在释放内存之前完成所有异步 __init 代码 */
	async_synchronize_full();

	system_state = SYSTEM_FREEING_INITMEM;
	kprobe_free_init_mem();
	ftrace_free_init_mem();
	kgdb_free_init_mem();
	exit_boot_config();
	free_initmem();
	mark_readonly();

	/*
	 * 内核映射现在已经最终化 - 更新用户空间页表
	 * 以完成PTI。
	 */
	pti_finalize();

	system_state = SYSTEM_RUNNING;
	numa_default_policy();

	rcu_end_inkernel_boot();

	do_sysctl_args();

	if (ramdisk_execute_command) {
		ret = run_init_process(ramdisk_execute_command);
		if (!ret)
			return 0;
		pr_err("Failed to execute %s (error %d)\n",
		       ramdisk_execute_command, ret);
	}

	/*
	 * 我们尝试每个命令,直到一个成功为止。
	 *
	 * 如果我们尝试恢复一个真正损坏的机器,则可以使用 Bourne shell 代替 init。
	 */
	if (execute_command) {
		ret = run_init_process(execute_command);
		if (!ret)
			return 0;
		panic("Requested init %s failed (error %d).",
		      execute_command, ret);
	}

	if (CONFIG_DEFAULT_INIT[0] != '\0') {
		ret = run_init_process(CONFIG_DEFAULT_INIT);
		if (ret)
			pr_err("Default init %s failed (error %d)\n",
			       CONFIG_DEFAULT_INIT, ret);
		else
			return 0;
	}

	if (!try_to_run_init_process("/sbin/init") ||
	    !try_to_run_init_process("/etc/init") ||
	    !try_to_run_init_process("/bin/init") ||
	    !try_to_run_init_process("/bin/sh"))
		return 0;

	panic("No working init found.  Try passing init= option to kernel. "
	      "See Linux Documentation/admin-guide/init.rst for guidance.");
}

这里主要功能包括等待kthreadd线程设置完成、执行内核初始化的可释放部分、完成异步初始化代码、释放初始化内存、最终化内核映射、设置系统状态为运行中等;

然后尝试**执行一系列的init命令(二进制可执行文件),直到找到一个可行的初始化命令为止,**如果都失败,则会触发内核panic。

common/init/main.c - try_to_run_init_process()

static int try_to_run_init_process(const char *init_filename)
{
	int ret;

	ret = run_init_process(init_filename);

	if (ret && ret != -ENOENT) {
		pr_err("Starting init: %s exists but couldn't execute it (error %d)\n",
		       init_filename, ret);
	}

	return ret;
}

try_to_run_init_process会进行run_init_process的调用,在调用失败的时候打印错误信息。

common/init/main.c - run_init_process()

static int run_init_process(const char *init_filename)
{
	const char *const *p;

	argv_init[0] = init_filename;
	pr_info("Run %s as init process\n", init_filename);
	pr_debug("  with arguments:\n");
	for (p = argv_init; *p; p++)
		pr_debug("    %s\n", *p);
	pr_debug("  with environment:\n");
	for (p = envp_init; *p; p++)
		pr_debug("    %s\n", *p);
	return kernel_execve(init_filename, argv_init, envp_init);
}

这段代码是用于运行初始化进程的函数。通过 kernel_execve 函数来执行该程序。在执行之前,打印出了一些要执行的命令和环境变量信息,以便进行调试和跟踪。

PS

  1. 因对整个kernel的知识还未完全掌握,没有解读start_kernel之前的代码流程和kernel_execve执行程序的细节;
  2. 文章仅针对从start_kernel到启动Init的进程的流程进行梳理,如有需要补充或者错误的地方,欢迎即使指正;
  3. 后续如果笔者能够学会kernel前的知识,如汇编、ld链接脚本等等,再进行补充;对于其他启动,仅做了简单注释;

参考

  1. 内核概览 | Android 开源项目 | Android Open Source Project (google.cn)
  2. kernel/superproject - Android Code Search
  3. Android启动过程–Kernel启动和init启动_安卓启动逻辑-优快云博客
[kernel 启动流程] (第一章)概述 建议先参考《[kernel 启动流程] 前篇——vmlinux.lds分析》等文章。一、kernel启动之前的准备动作在kernel启动之前的准备都是由bootloader来完成。所以不管是什么bootloader,例如uboot、LK、superboot等等,都需要实现以下准备动作。这里指说明概念,不涉及代码。 我们在project X项目中使用的bootloader是uboot,具体代码参考第 阅读详情

相关推荐

kernel启动流程-start_kernel的执行_1.概述

1.前言 本专题文章承接之前《kernel启动流程_head.S的执行》专题文章,我们知道在head.S执行过程中保存了bootloader传递的启动参数、启动模式以及FDT地址等,创建了内核空间的页表,最后为init进程初始化好了堆栈,并跳转到start_kernel执行。 本专题文章就是简要介绍start_kernel的启动流程,并对进程、内存、IO等的初始化部分做重点分析,本文是start_kernel的概述部分,总体介绍start_kernel包含的主要流程。 kernel版本:5.10 平台:ar

HZero 7220

Android Kernel 编译与调试指北

经过一步步的摸索,将Linux内核成功运行到了Cuttlefish中,在Cuttlefish的环境下,可以调试内核,调试Native代码,调试Framework代码,也可以当作你的常用“开发机”使用。该文章是笔者学习中的一个总结,由于知识面的狭隘总有认识不到的错误产生,请大家不吝赐教。如果大家学习中遇见问题,也欢迎大家交流沟通。

Eqiqi的博客 4434

基于Android 14系统启动流程之zygote进程启动解析 (详细分析从kernel启动到zygote启动完了的过程)

本篇文章主要讲解zygote是如何启动的,从内核kernel启动开始,一直贯穿到java层代码,包括以下几部分: 1、init 进程的启动时机 2、init 进程都做了哪些事情 3、zygote进程启动的时机,都做了哪些内容 4、java代码是如何运行的 5、简单画了函数之间的调用关系。

qq_15125719的博客 2359

Android的系统架构的认识

文章目录1.Android 系统架构1.1 Kernel 层(Linux内核层)1.2. Native层(系统运行库层)1.3. Framework (应用框架层)1.4. App(应用层)2.Android 系统启动架构2.1 Loader 层2.2 Kernel 层2.3 Native 层2.4 Framework 层Zygote进程System Server 进程Media Server 进程2.5 App层Syscall && JNI 1.Android 系统架构 Android底层

haazzz的博客 1537

叶绿体基因组提交NCBI全流程避坑指南

叶绿体基因组作为环状细胞器DNA,其数据提交并非简单上传,而是需严格遵循INSDC标准的学术出版行为。其核心原理在于元数据完整性、拓扑结构语义一致性与分类学路径精确匹配;技术价值体现在保障序列可追溯、可复现、可互操作;典型应用场景涵盖植物系统发育、条形码开发及合成生物学底盘构建。实践中,FASTA头行格式、feature table嵌套规则、taxid绑定及BankIt静默校验等环节极易引发失败。本文聚焦NCBI|叶绿体基因组数据上传中的高频报错与隐性约束,提供面向生物信息工程师的实操级规范解析。

weixin_31960565的博客 326

C++面向对象程序设计大作业:魔兽世界(三):开战

C++面向对象程序设计大作业:魔兽世界(三):开战问题描述问题分析代码 问题描述 问题来自于北京大学郭炜老师的C++慕课的大作业 魔兽世界的西面是红魔军的司令部,东面是蓝魔军的司令部。两个司令部之间是依次排列的若干城市,城市从西向东依次编号为1,2,3 … N ( N <= 20)。红魔军的司令部算作编号为0的城市,蓝魔军的司令部算作编号为N+1的城市。司令部有生命元,用于制造武士。 两军的司令部都会制造武士。武士一共有dragon 、ninja、iceman、lion、wolf 五种。每种武士都有

RedRum_1965的博客 4036

【Android系统启动】 BootLoader - Little Kernel启动流程源码解析

后文中的lk指 Little KernelAndroid启动会经过BootLoader阶段, BootLoader中, 挑选little kernel作为bootloader程序进行源码研究。当然也许多公司使用的是uboot,像作者公司许多项目也是使用uboot。lk官方源码中,没有集成高通、MTK平台的代码, 但是在github开源代码中,能够找到适配高通平台和MTK平台的lk,本文先是基于官方开源代码lk进行总体的分析, 然后对高通平台lk、MTK平台lk的不同部分进行分析;

thirdbro的博客 1867

华为嵌入式面试题及参考答案(持续更新)

异步是指在程序执行过程中,某些任务不需要等待立即完成就可以继续执行其他任务的过程。在计算机编程中,异步通常用于处理长时间运行的操作,如文件读写、网络通信等,以避免阻塞主线程。异步处理通常涉及到回调函数、事件循环、协程或未来的概念。使用宏定义来避免命名冲突。#else#endif堆和栈是计算机程序中用于存储数据的两种不同类型的内存区域。

大模型大数据攻城狮的专栏 2484

Android系统启动流程 -- linux kernel

第二部分:linux启动   一、zImage是怎样炼成的?     zImage是linux内核编译之后产生的最终文件,它的生成过程比较复杂,这里不谈编译过程,只聊聊编译的最后阶段:     1.  arm-linux-gnu-ld用arch/arm/kernel/vmlinux.lds、arch/arm/kernel/head.o、 arch/arm/kernel/init_task

zgolee的专栏 9968

“华为杯”研究生数学建模竞赛2017年-【华为杯】B题:面向下一代光通信的 VCSEL 激光器仿真模型

得出以下主要结论:在相同的偏置电流条件下,随着温度的升高,带宽的峰值向。在相同的温度下,随着电流的增大,带宽的峰值越小且曲线由波峰形状。方案,包括可能的数学公式,不同温度和偏置电流下的带宽响应曲线,并与问题。线,得出以下主要结论:在相同的偏置电流条件下,随着温度的升高,带宽的峰。在相同的温度下,随着电流的增大,带宽的峰值越小且曲线由波峰形状逐渐变得。得的激光器特性曲线和模型中仿真得到的特性曲线,之后,可以将两组曲线的差。说明的是,由于数值优化算法的成功与否很大程度上依赖于初始参数的猜测,参。

getusushu的博客 98

OWASP-ZAP扫描器的使用

目录 OWASP-ZAP 更新 代理 目录扫描 主动扫描(Active Scan) 扫描结果 生成报告 OWASP-ZAP OWASP Zed攻击代理(ZAP)是世界上最受欢迎的免费安全审计工具之一,由数百名国际志愿者积极维护。它可以帮助你在开发和测试应用程序时自动查找Web应用程序中的安全漏洞。 也可以说ZAP是一个中间人代理。它能够获取你对Web应用程序发出的所有请求以...

谢公子的博客 2万+

Android系统启动流程 -- bootloader、linux kernel、android

转载:http://blog.youkuaiyun.com/arnoldlu/article/details/8264721 目录:        第一部分:Bootloader启动 一、Bootloader的定义和种类 二、Arm特定平台的Bootloader 三、U-boot启动流程分析

kylin_fire_zeng的专栏 5064

【AI面试】 Python 的装饰器

Python装饰器是一种高阶函数,用于在不修改原函数代码的情况下增强其功能。它通过@语法糖接收目标函数作为参数,并返回一个包含额外逻辑(如日志、缓存、权限验证等)的新函数。装饰器的核心价值在于代码复用和解耦,符合开闭原则,典型应用包括性能分析、权限控制等场景。例如,@timer_decorator可自动为函数添加耗时统计功能。本质上,@decorator等价于func = decorator(func),实际调用的是装饰器返回的包装函数。进阶技巧包括使用@functools.wraps保留原函数元信息。装饰

钱多多先森 184

Linux启动流程-bootloader至kernel的过程--android系统启动流程

1 Bootloader 对于一般的ARM处理器,CPU上电或复位执行第一条指令所在地址,即第一段程序Bootloader的开始地址,Bootloader一般存于Nor-flash(XIP),支持芯片内执行。 Bootloader的功能可总结为:1)初始化CPU时钟,内存,串口等。2)设置Linux启动参数。3)加载Linux内核镜像。Android源码位于根目录下的/bootable文件夹。

tianshiyalin的专栏 3374

Dify 本地部署教程

到这里,Dify 就安装完成了,更多操作设置可以参考官方文档,Dify 和 FastGPT 基本上差不多,也可以同时安装一下 FastGPT 进行体验~Docker Compose 部署 | Dify。

Muti-Agent 6652

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Android系统启动流程 -- linux kernel

http://blog.youkuaiyun.com/lizhiguo0532/article/details/7028868  第二部分:linux启动   一、zImage是怎样炼成的?     zImage是linux内核编译之后产生的最终文件,它的生成过程比较复杂,这里不谈编译过程,只聊聊编译的最后阶段:     1.  arm-linux-gnu-ld用arch/arm/kernel/vm

深度理解是快乐的源泉 578

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Android系统启动流程二之linux kernel

转载别人的文章,讲的非常详细,整个android的启动过程!

红旺永福 582

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

层启动流程_Android系统启动流程

点击上方蓝字关注我,每天一见,给你力量前言今天说《Android体系架构》第一篇内容,关于Android系统启动的过程。图解之前我们说过Android系统的架构图(图片来自参考链接),这里再贴一下:而我们的Android系统启动的过程就是架构图中从下往上运行加载的过程,这里有一张关于Android系统启动过程的总结图(图片来自参考链接gityuan.com),大家可以先看看:系统启动过程...

weixin_33622085的博客 258

Android 8.0 系统启动流程之Linux内核启动--kernel_init进程(三)

    在上一篇文章中详细的分析了kthreadd进程的启动,init进程也是有idle进程去触发启动的,init进程分为前后两部分,前一部分是在内核启动的,主要是完成创建和内核初始化工作,内容都是跟Linux内核相关的;后一部分是在用户空间启动的,主要完成Android系统的初始化工作。     本文着重分析init进程的前一部分,init进程的后一部分将在下一篇文章中讲述。 1、init进...

marshal_zsx的博客 4799

android str流程,Android arm linux kernel启动流程一

虽然这里的Arm Linux kernel前面加上了Android,但实际上还是和普遍Arm linux kernel启动的过程一样的,这里只是结合一下Android的Makefile,讲一下bootimage生成的一个过程。这篇文档主要描述bootimage的构造,以及kernel真正执行前的解压过程。在了解这些之前我们首先需要了解几个名词,这些名词定义在/Documentation/arm/P...

weixin_35851515的博客 1622
上一篇: 【Android系统启动】 BootLoader - Little Kernel启动流程源码解析
下一篇: 【Android系统启动】 Android Init 语言官网教程及中文翻译
LokiCode
博客等级 码龄3年 839粉丝 18原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LokiCode

谢谢您买的奶茶

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值