mirror of
https://github.com/feicong/rom-course.git
synced 2025-05-05 18:16:57 +00:00
第三章 android启动流程 内核启动
This commit is contained in:
parent
2b1c9a7e0d
commit
db43dc471a
@ -96,7 +96,7 @@ wsl --import ubuntu22 E:\wsl2\ubuntu22_wsl E:\wsl2\ubuntu22.tar
|
||||
|
||||

|
||||
|
||||
7、虚拟硬盘分配,这里至少分配300G的空间,考虑到性能,我选择的是单文件吗,这里如果选择立即分配所有磁盘空间,能提高一定的性能。如果你的电脑配置不是很高,建议你选择立即分配。
|
||||
7、虚拟硬盘分配,这里至少分配500G的空间,考虑到性能,我选择的是单文件吗,这里如果选择立即分配所有磁盘空间,能提高一定的性能。如果你的电脑配置不是很高,建议你选择立即分配。
|
||||
|
||||

|
||||
|
||||
@ -1166,15 +1166,29 @@ device/mediatek/wembley-sepolicy: sleeping 4.0 seconds before retrying
|
||||
<project name="device/mediatek/wembley-sepolicy" path="device/mediatek/wembley-sepolicy" groups="device"/>
|
||||
~~~
|
||||
|
||||
然后看了创建仓库和批量提交代码的逻辑就明白了,是的,name和path的顺序反了,导致正则表达式未能成功匹配到这个仓库,所以我们调整一下name和path的顺序即可。到这里就完成了gitlab源码管理android源码开发了。最后如何使用git提交和查看历史记录我就不在这里叙述了。
|
||||
然后看了创建仓库和批量提交代码的逻辑就明白了,是的,name和path的顺序反了,导致正则表达式未能成功匹配到这个仓库,所以我们调整一下name和path的顺序即可。
|
||||
|
||||
|
||||
成功拉取完成后,如果在编译时碰到找不到文件的问题,这是由于有些子模块仓库下的子目录中有`.gitignore`文件,将一些应该提交的文件给过滤掉了。就回到我们同步代码的目录中,找到指定的git仓库,使用下面的方式重新提交一下。然后回到我们同步下来的代码处重新拉取更新的代码。
|
||||
|
||||
~~~
|
||||
// 进入缺少文件的子模块仓库目录
|
||||
cd ~/external/angle/
|
||||
git add . -f
|
||||
git commit -m "init"
|
||||
git push -u origin master
|
||||
cd ~/android_src/myandroid12/
|
||||
repo sync -j8
|
||||
~~~
|
||||
|
||||
到这里就完成了gitlab源码管理android源码开发了。最后如何使用git提交和查看历史记录我就不在这里叙述了。
|
||||
|
||||
## 2.10 小结
|
||||
|
||||
在这一章里,主要讲述了如何从零开始搭建一个编译Android源码的环境,以及如何选择编译的版本和完整的编译Android源码并使用自己编译的内核,然后将这个我们编译好的镜像尝试多种方式刷入测试手机中。为了后续开发和阅览代码的便利,又讲述了如何使用Android Studio和Clion导入源码。最后为了便于长期维护和持续性的开发,我们又搭建了gitlab+repo管理Android源码。终于将一切准备就绪了。那么接下来,让我们在下一章开始解开Android源码神秘的面纱。
|
||||
在这一章里,主要讲述了如何从零开始搭建一个编译Android源码的环境,以及如何选择编译的版本和完整的编译Android源码并使用自己编译的内核,然后将这个我们编译好的镜像尝试多种方式刷入测试手机中。为了后续开发和阅览代码的便利,又讲述了如何使用Android Studio和Clion导入源码。最后为了便于长期维护和持续性的开发,我们又搭建了gitlab+repo管理Android源码。终于将一切准备就绪了。
|
||||
|
||||
如果你理解了源码同步的方式,那么你可以延申一下,如何将源码直接同步到github中呢?请自己动手试试吧。
|
||||
|
||||
|
||||
|
||||
TODO 你看看这里是不是小结写的有点简单。看着应该怎么丰富一下。
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
2、bionic:这是Android的C库,包含了很多标准的C库函数和头文件,还有一些Android特有的函数和头文件。
|
||||
|
||||
3、bootable:该目录包含了引导程序,这些引导程序用于从系统启动,并初始化硬件, 例如Bootloader和Recovery程序。
|
||||
3、bootable:包含了一些用于生成引导程序相关代码的工具和脚本,以及引导程序相关的一些源代码,但并不包含完整的引导程序代码。
|
||||
|
||||
4、build:该目录包含了编译Android源代码所需要的脚本,包括makefile文件和一些构建工具。
|
||||
|
||||
@ -62,4 +62,466 @@
|
||||
|
||||

|
||||
|
||||
下载两个驱动文件后,我们将文件放到源码根目录中解压,并且执行相应的sh脚本进行导出,到了这里我们了解到vendor中是硬件厂商提供的摄像头蓝牙之类的驱动程序。那么我们就可以观察到,脚本执行后,实际就是将驱动文件放到了对应目录中。
|
||||
下载两个驱动文件后,我们将文件放到源码根目录中解压,并且执行相应的sh脚本进行导出,到了这里我们了解到vendor中是硬件厂商提供的摄像头蓝牙之类的驱动程序。那么我们就可以观察到,脚本执行后,实际就是将驱动文件放到了对应目录中。对根目录结构有一个简单的了解之后,我们就可以开始翻阅源码,翻阅源码我们可以通过前面搭建好的开发环境,或者是使用在线的源码查看网站http://aospxref.com/。
|
||||
|
||||
## 3.2 Android启动流程
|
||||
|
||||
Android启动流程主要分为四个阶段:Bootloader阶段、Kernel阶段、Init进程阶段和System Server启动阶段,首先我们先简单介绍下这几个阶段的启动流程。
|
||||
|
||||
1. Bootloader阶段: 当手机或平板电脑开机时,首先会执行引导加载程序(Bootloader),它会在手机的ROM中寻找启动内核(Kernel)的镜像文件,并将其加载进RAM中。在这个阶段中,Android系统并没有完全启动,只是建立了基本的硬件和内核环境。
|
||||
2. Kernel阶段: Kernel阶段是Android启动流程的第二阶段,它主要负责初始化硬件设备、加载驱动程序、设置内存管理等。此外,Kernel还会加载initramfs,它是一个临时文件系统,包含了init程序和一些设备文件。
|
||||
3. Init进程阶段: Kernel会启动init进程,它是Android系统中的第一个用户空间进程。Init进程的主要任务是读取init.rc文件,并根据该文件中的配置信息启动和配置Android系统的各个组件。在这个阶段中,系统会依次启动各个服务和进程,包括启动Zygote进程和创建System Server进程。
|
||||
4. System Server启动阶段: System Server是Android系统的核心服务进程,它会启动所有的系统服务。其中包括Activity Manager、Package Manager、Window Manager、Location Manager、Telephony Manager、Wi-Fi Service、Bluetooth Service等。System Server启动后,Android系统就完全启动了,用户可以进入桌面,开始使用各种应用程序。
|
||||
|
||||
## 3.3 内核启动
|
||||
|
||||
Bootloader其实就是一段程序,这个程序的主要功能就是用来引导系统启动所以我们也称之为引导程序,而这个引导程序是存放在一个只读的寄存器中,从物理地址0开始的一段空间分配给了这个只读存储器来存放引导程序。
|
||||
|
||||
Bootloader会初始化硬件设备并准备内存空间映射,为启动内核准备环境。然后寻找内核的镜像文件,验证boot分区和recovery分区的完整性,然后将其加载到内存中,最后开始执行内核。我们可以通过命令`adb reboot bootloader`直接重启进入引导程序。
|
||||
|
||||
Bootloader 将件初始化完成后,会在特定的物理地址处查找 EFI 引导头(efi_head)。如果查找到 EFI 引导头,bootloader 就会加载 EFI 引导头指定的 EFI 引导程序,然后开始执行 EFI 引导程序,以进行后续的 EFI 引导流程。而这个efi_head就是linux内核最早的入口了。
|
||||
|
||||
这里注意,我们现在并需要完全看懂内核中的汇编部分代码,主要是为了解执行的流程,所以并不需要你有汇编的功底,只需要能看懂简单的几个指令即可,接下来打开我们编译内核源码时的目录,找到文件`~/android_src/android-kernel/private/msm-google/arch/arm64/kernel/head.S`查看汇编代码如下。
|
||||
|
||||
~~~assembly
|
||||
__HEAD
|
||||
_head:
|
||||
/*
|
||||
* DO NOT MODIFY. Image header expected by Linux boot-loaders.
|
||||
*/
|
||||
#ifdef CONFIG_EFI
|
||||
/*
|
||||
* This add instruction has no meaningful effect except that
|
||||
* its opcode forms the magic "MZ" signature required by UEFI.
|
||||
*/
|
||||
add x13, x18, #0x16
|
||||
b stext
|
||||
#else
|
||||
b stext // branch to kernel start, magic
|
||||
~~~
|
||||
|
||||
在arm指令集中,指令b表示跳转,所以我们继续找到stext的定义部分。
|
||||
|
||||
~~~assembly
|
||||
/*
|
||||
* The following callee saved general purpose registers are used on the
|
||||
* primary lowlevel boot path:
|
||||
*
|
||||
* Register Scope Purpose
|
||||
* x21 stext() .. start_kernel() FDT pointer passed at boot in x0
|
||||
* x23 stext() .. start_kernel() physical misalignment/KASLR offset
|
||||
* x28 __create_page_tables() callee preserved temp register
|
||||
* x19/x20 __primary_switch() callee preserved temp registers
|
||||
*/
|
||||
ENTRY(stext)
|
||||
bl preserve_boot_args //把引导程序传的4个参数保存在全局数组boot_args
|
||||
bl el2_setup // Drop to EL1, w0=cpu_boot_mode
|
||||
adrp x23, __PHYS_OFFSET
|
||||
and x23, x23, MIN_KIMG_ALIGN - 1 // KASLR offset, defaults to 0
|
||||
bl set_cpu_boot_mode_flag
|
||||
bl __create_page_tables // 创建页表映射 x25=TTBR0, x26=TTBR1
|
||||
/*
|
||||
* The following calls CPU setup code, see arch/arm64/mm/proc.S for
|
||||
* details.
|
||||
* On return, the CPU will be ready for the MMU to be turned on and
|
||||
* the TCR will have been set.
|
||||
*/
|
||||
bl __cpu_setup // // 初始化处理器 initialise processor
|
||||
b __primary_switch
|
||||
ENDPROC(stext)
|
||||
~~~
|
||||
|
||||
能看到最后一行是跳转到__primary_switch,接下来继续看它的实现代码
|
||||
|
||||
~~~assembly
|
||||
__primary_switch:
|
||||
#ifdef CONFIG_RANDOMIZE_BASE
|
||||
mov x19, x0 // preserve new SCTLR_EL1 value
|
||||
mrs x20, sctlr_el1 // preserve old SCTLR_EL1 value
|
||||
#endif
|
||||
|
||||
bl __enable_mmu
|
||||
#ifdef CONFIG_RELOCATABLE
|
||||
bl __relocate_kernel
|
||||
#ifdef CONFIG_RANDOMIZE_BASE
|
||||
ldr x8, =__primary_switched //将x8设置成__primary_switched的地址
|
||||
adrp x0, __PHYS_OFFSET
|
||||
blr x8 //调用__primary_switched
|
||||
|
||||
/*
|
||||
* If we return here, we have a KASLR displacement in x23 which we need
|
||||
* to take into account by discarding the current kernel mapping and
|
||||
* creating a new one.
|
||||
*/
|
||||
msr sctlr_el1, x20 // disable the MMU
|
||||
isb
|
||||
bl __create_page_tables // recreate kernel mapping
|
||||
|
||||
tlbi vmalle1 // Remove any stale TLB entries
|
||||
dsb nsh
|
||||
isb
|
||||
|
||||
msr sctlr_el1, x19 // re-enable the MMU
|
||||
isb
|
||||
ic iallu // flush instructions fetched
|
||||
dsb nsh // via old mapping
|
||||
isb
|
||||
|
||||
bl __relocate_kernel
|
||||
#endif
|
||||
#endif
|
||||
ldr x8, =__primary_switched
|
||||
adrp x0, __PHYS_OFFSET
|
||||
br x8
|
||||
ENDPROC(__primary_switch)
|
||||
~~~
|
||||
|
||||
接着我们继续跟踪__primary_switched函数,然后我们就能看到一个重点函数start_kernel了。
|
||||
|
||||
~~~assembly
|
||||
__primary_switched:
|
||||
adrp x4, init_thread_union
|
||||
add sp, x4, #THREAD_SIZE
|
||||
adr_l x5, init_task
|
||||
msr sp_el0, x5 // Save thread_info
|
||||
|
||||
adr_l x8, vectors // load VBAR_EL1 with virtual
|
||||
msr vbar_el1, x8 // vector table address
|
||||
isb
|
||||
|
||||
stp xzr, x30, [sp, #-16]!
|
||||
mov x29, sp
|
||||
|
||||
#ifdef CONFIG_SHADOW_CALL_STACK
|
||||
adr_l x18, init_shadow_call_stack // Set shadow call stack
|
||||
#endif
|
||||
|
||||
str_l x21, __fdt_pointer, x5 // Save FDT pointer
|
||||
|
||||
ldr_l x4, kimage_vaddr // Save the offset between
|
||||
sub x4, x4, x0 // the kernel virtual and
|
||||
str_l x4, kimage_voffset, x5 // physical mappings
|
||||
|
||||
// Clear BSS
|
||||
adr_l x0, __bss_start
|
||||
mov x1, xzr
|
||||
adr_l x2, __bss_stop
|
||||
sub x2, x2, x0
|
||||
bl __pi_memset
|
||||
dsb ishst // Make zero page visible to PTW
|
||||
#ifdef CONFIG_KASAN
|
||||
bl kasan_early_init
|
||||
#endif
|
||||
#ifdef CONFIG_RANDOMIZE_BASE
|
||||
tst x23, ~(MIN_KIMG_ALIGN - 1) // already running randomized?
|
||||
b.ne 0f
|
||||
mov x0, x21 // pass FDT address in x0
|
||||
mov x1, x23 // pass modulo offset in x1
|
||||
bl kaslr_early_init // parse FDT for KASLR options
|
||||
cbz x0, 0f // KASLR disabled? just proceed
|
||||
orr x23, x23, x0 // record KASLR offset
|
||||
ldp x29, x30, [sp], #16 // we must enable KASLR, return
|
||||
ret // to __primary_switch()
|
||||
0:
|
||||
#endif
|
||||
b start_kernel // 内核的入口函数
|
||||
ENDPROC(__primary_switched)
|
||||
~~~
|
||||
|
||||
上面能看到最后一个指令就是start_kernel了,这个函数是内核的入口函数,同时也是c语言部分的入口函数。接下来我们查看文件`~/android_src/android-kernel/private/msm-google/init/main.c`,可以看到其中大量的init初始化各种子系统的函数调用。
|
||||
|
||||
~~~c
|
||||
asmlinkage __visible void __init start_kernel(void)
|
||||
{
|
||||
char *command_line;
|
||||
char *after_dashes;
|
||||
|
||||
set_task_stack_end_magic(&init_task);
|
||||
scs_set_init_magic(&init_task);
|
||||
|
||||
smp_setup_processor_id();
|
||||
debug_objects_early_init();
|
||||
|
||||
cgroup_init_early();
|
||||
|
||||
local_irq_disable();
|
||||
early_boot_irqs_disabled = true;
|
||||
|
||||
/*
|
||||
* Interrupts are still disabled. Do necessary setups, then
|
||||
* enable them
|
||||
*/
|
||||
boot_cpu_init();
|
||||
page_address_init();
|
||||
pr_notice("%s", linux_banner);
|
||||
setup_arch(&command_line);
|
||||
/*
|
||||
* Set up the the initial canary ASAP:
|
||||
*/
|
||||
boot_init_stack_canary();
|
||||
mm_init_cpumask(&init_mm);
|
||||
setup_command_line(command_line);
|
||||
setup_nr_cpu_ids();
|
||||
setup_per_cpu_areas();
|
||||
smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */
|
||||
boot_cpu_hotplug_init();
|
||||
|
||||
build_all_zonelists(NULL, NULL, false);
|
||||
page_alloc_init();
|
||||
|
||||
pr_notice("Kernel command line: %s\n", boot_command_line);
|
||||
/* parameters may set static keys */
|
||||
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);
|
||||
if (!IS_ERR_OR_NULL(after_dashes))
|
||||
parse_args("Setting init args", after_dashes, NULL, 0, -1, -1,
|
||||
NULL, set_init_arg);
|
||||
|
||||
/*
|
||||
* These use large bootmem allocations and must precede
|
||||
* kmem_cache_init()
|
||||
*/
|
||||
setup_log_buf(0);
|
||||
pidhash_init();
|
||||
vfs_caches_init_early();
|
||||
sort_main_extable();
|
||||
trap_init();
|
||||
mm_init();
|
||||
|
||||
/*
|
||||
* Set up the scheduler prior starting any interrupts (such as the
|
||||
* timer interrupt). Full topology setup happens at smp_init()
|
||||
* time - but meanwhile we still have a functioning scheduler.
|
||||
*/
|
||||
sched_init();
|
||||
/*
|
||||
* Disable preemption - early bootup scheduling is extremely
|
||||
* fragile until we cpu_idle() for the first time.
|
||||
*/
|
||||
preempt_disable();
|
||||
if (WARN(!irqs_disabled(),
|
||||
"Interrupts were enabled *very* early, fixing it\n"))
|
||||
local_irq_disable();
|
||||
idr_init_cache();
|
||||
|
||||
/*
|
||||
* Allow workqueue creation and work item queueing/cancelling
|
||||
* early. Work item execution depends on kthreads and starts after
|
||||
* workqueue_init().
|
||||
*/
|
||||
workqueue_init_early();
|
||||
|
||||
rcu_init();
|
||||
|
||||
/* trace_printk() and trace points may be used after this */
|
||||
trace_init();
|
||||
|
||||
context_tracking_init();
|
||||
radix_tree_init();
|
||||
/* init some links before init_ISA_irqs() */
|
||||
early_irq_init();
|
||||
init_IRQ();
|
||||
tick_init();
|
||||
rcu_init_nohz();
|
||||
init_timers();
|
||||
hrtimers_init();
|
||||
softirq_init();
|
||||
timekeeping_init();
|
||||
time_init();
|
||||
sched_clock_postinit();
|
||||
printk_nmi_init();
|
||||
perf_event_init();
|
||||
profile_init();
|
||||
call_function_init();
|
||||
WARN(!irqs_disabled(), "Interrupts were enabled early\n");
|
||||
early_boot_irqs_disabled = false;
|
||||
local_irq_enable();
|
||||
|
||||
kmem_cache_init_late();
|
||||
|
||||
/*
|
||||
* HACK ALERT! This is early. We're enabling the console before
|
||||
* we've done PCI setups etc, and console_init() must be aware of
|
||||
* this. But we do want output early, in case something goes wrong.
|
||||
*/
|
||||
console_init();
|
||||
if (panic_later)
|
||||
panic("Too many boot %s vars at `%s'", panic_later,
|
||||
panic_param);
|
||||
|
||||
lockdep_info();
|
||||
|
||||
/*
|
||||
* Need to run this when irqs are enabled, because it wants
|
||||
* to self-test [hard/soft]-irqs on/off lock inversion bugs
|
||||
* too:
|
||||
*/
|
||||
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
|
||||
page_ext_init();
|
||||
debug_objects_mem_init();
|
||||
kmemleak_init();
|
||||
setup_per_cpu_pageset();
|
||||
numa_policy_init();
|
||||
if (late_time_init)
|
||||
late_time_init();
|
||||
sched_clock_init();
|
||||
calibrate_delay();
|
||||
pidmap_init();
|
||||
anon_vma_init();
|
||||
acpi_early_init();
|
||||
#ifdef CONFIG_X86
|
||||
if (efi_enabled(EFI_RUNTIME_SERVICES))
|
||||
efi_enter_virtual_mode();
|
||||
#endif
|
||||
#ifdef CONFIG_X86_ESPFIX64
|
||||
/* Should be run before the first non-init thread is created */
|
||||
init_espfix_bsp();
|
||||
#endif
|
||||
thread_stack_cache_init();
|
||||
cred_init();
|
||||
fork_init();
|
||||
proc_caches_init();
|
||||
buffer_init();
|
||||
key_init();
|
||||
security_init();
|
||||
dbg_late_init();
|
||||
vfs_caches_init();
|
||||
pagecache_init();
|
||||
signals_init();
|
||||
proc_root_init();
|
||||
nsfs_init();
|
||||
cpuset_init();
|
||||
cgroup_init();
|
||||
taskstats_init_early();
|
||||
delayacct_init();
|
||||
|
||||
check_bugs();
|
||||
|
||||
acpi_subsystem_init();
|
||||
sfi_init_late();
|
||||
|
||||
if (efi_enabled(EFI_RUNTIME_SERVICES)) {
|
||||
efi_late_init();
|
||||
efi_free_boot_services();
|
||||
}
|
||||
|
||||
ftrace_init();
|
||||
|
||||
/* Do the rest non-__init'ed, we're now alive */
|
||||
rest_init();
|
||||
|
||||
prevent_tail_call_optimization();
|
||||
}
|
||||
~~~
|
||||
|
||||
这里我们继续追踪关键的函数rest_init,就是在这里开启的内核初始化线程以及创建内核线程。
|
||||
|
||||
~~~c
|
||||
static noinline void __ref rest_init(void)
|
||||
{
|
||||
...
|
||||
kernel_thread(kernel_init, NULL, CLONE_FS);
|
||||
numa_default_policy();
|
||||
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
|
||||
...
|
||||
}
|
||||
~~~
|
||||
|
||||
接着看看kernel_init线程执行的内容。
|
||||
|
||||
~~~c
|
||||
static int __ref kernel_init(void *unused)
|
||||
{
|
||||
int ret;
|
||||
|
||||
kernel_init_freeable();
|
||||
/* need to finish all async __init code before freeing the memory */
|
||||
async_synchronize_full();
|
||||
free_initmem();
|
||||
mark_readonly();
|
||||
system_state = SYSTEM_RUNNING;
|
||||
numa_default_policy();
|
||||
|
||||
rcu_end_inkernel_boot();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* We try each of these until one succeeds.
|
||||
*
|
||||
* The Bourne shell can be used instead of init if we are
|
||||
* trying to recover a really broken machine.
|
||||
*/
|
||||
if (execute_command) {
|
||||
ret = run_init_process(execute_command);
|
||||
if (!ret)
|
||||
return 0;
|
||||
panic("Requested init %s failed (error %d).",
|
||||
execute_command, ret);
|
||||
}
|
||||
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/init.txt for guidance.");
|
||||
}
|
||||
~~~
|
||||
|
||||
在这里我们看到了原来init进程是用try_to_run_init_process启动的,并且会依次执行上面的4个进程。我们继续看看这个函数是如何启动进程的。
|
||||
|
||||
~~~c
|
||||
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;
|
||||
}
|
||||
~~~
|
||||
|
||||
这里简单包装调用的run_init_process,继续看下面的代码
|
||||
|
||||
~~~c
|
||||
static int run_init_process(const char *init_filename)
|
||||
{
|
||||
argv_init[0] = init_filename;
|
||||
return do_execve(getname_kernel(init_filename),
|
||||
(const char __user *const __user *)argv_init,
|
||||
(const char __user *const __user *)envp_init);
|
||||
}
|
||||
~~~
|
||||
|
||||
这里能看到最后是通过execve拉起来的init进程。到这里内核就成功拉起了在最后,我们总结内核启动的简单流程图如下。
|
||||
|
||||

|
||||
|
||||
## 3.4 Init进程启动
|
||||
|
BIN
chapter-03/images/startkernel.png
Normal file
BIN
chapter-03/images/startkernel.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
1
chapter-03/startkernel.drawio
Normal file
1
chapter-03/startkernel.drawio
Normal file
@ -0,0 +1 @@
|
||||
<mxfile host="Electron" modified="2023-02-21T15:02:33.543Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.9.9 Chrome/85.0.4183.121 Electron/10.1.5 Safari/537.36" etag="sEMhMPYwBkhyji3gxQ8t" version="13.9.9" type="device"><diagram id="QhyC4PTY8g-ZMk1R3MFR" name="第 1 页">1Vlbj9o4GP01fuwoFxzsx4RLV9qtVO087HZfkId4SaYhRo4p0F+/nx2HJBimzJaZ0NEI2Z8vOOec7+KAwsl6/1GyTfZJpLxAgZfuUThFQUBwCJ/acKgNI0xrw0rmaW3yW8Nj/p1bo2et2zzlVW+iEqJQ+aZvXIqy5EvVszEpxa4/7V9R9L91w1bcMTwuWeFa/8pTlVmrH9F24DeerzL71SQY1wNr1ky2T1JlLBW7jimcoXAihVB1a72f8EJj1+BSr5tfGD0eTPJSXbPgOfrzeR9m4tPvz0XC/3kKP5b7D5adSh2aB+YpPL/tCqkysRIlK2atNZFiW6Zc7+pBr53zhxAbMPpgfOZKHSyZbKsEmDK1Luwo3+fqb738Advel87IdG93Np2D7dTn1Ie7+PjWVImtXPIXntk/gg+i5WLNlTzAOskLpvJv/f2Zlc/qOO+49LPI4ZsDzyrdx/gh7P5ZHTS6D73+jorJFVd2k5Y2aHRO1ZoMma8g1p7/Gyu29onQLEKEIpLoBp2gJEYz6HqIQmOM6AglGM0wSibaeJxzqo8++7ssV/xxwwzgO4gAfabtIbhUfP8yby4fdsHY6+EYRra/63hjMyfrOGIYXWawB/ZrkSUusgP40A39AduwagT5wrzoJ/3mp1DHZ/SMEYlRDFolKJnCfwKxtBAs5dKMgdUzDdC9UXY8RZRYN4jHelnsIzK5N4kfQ8VgEm9y2MAaB/jkQSeKD96D548ay5c6dYyixtBmDNM7dHufucwBFJDEzfNIdKXf0Fvkm1hKduhM2OjEUV1OR0HUFxX2ab86eN18aNQnuGmSiq5wapOSKEqiM47bOneklyTk3lwZe0O7cogH91yP9j0XKqNhHZde6bjBaMiMR38cg99b3QE9KcYoceQdnJN30Bz+5ig198QOTEwuM9iLyXUEBM6/clnqO+k84yx9eHRABDhUHylW5KsS2kuAResv0aDlcCeM7cA6T9PaUXiVf2dPZitNgY3KsC9OEJ7qvcA3qtpNbkRCiE8rYpcEH58jwXsrDkYOBwsNNpjOhGiwgIzieRPqibl9xIiMjAXKNFOskTmK3dvpeys+HJ3UZs2zDhbQfbcSroyEz4NN9K0uGZvyd2wufyOdVMlUow6NeHZ/GI+HTpq+W/8uFhvIR0weFtUuV8sM8AmiQgeOJwmtlW4ZbAHwOC9zZaCfI5pooEHkGnqi4U5mLfQzo/a6yAHCSGSZI7WbTOwNhk71lfzeeMKD31N8N0ca6GEvlpcPSyCGrTUQ5VO1MSCc9n/xXBAR/MNcQN4zFQRuKhhct/j/Vi2h91ZVS+AKt4JyVC2aYsUNLefyKIQJOu2EdqwvRRS8bWKzrM6pYzM6bV6AeCb6hCgxgQk+6bz2mYFZiu4u04ZuaQk+rhY1XNczZIYA6CTuVDpj3YX80CYHpyy6O0bI0PE+dN9q1w7zak7OeY3Lyb0xgPHgDLivbGDbhRILuS0NC1AniSWvqvNs9CV+oU66N9ij4O1gh277E2D9Xq39HTWc/Qc=</diagram></mxfile>
|
20
目录.md
20
目录.md
@ -32,17 +32,19 @@
|
||||
第3章 认识系统组件
|
||||
3.1 源码结构介绍
|
||||
|
||||
3.2 进程启动流程
|
||||
3.2 Android的启动流程
|
||||
|
||||
3.3 init进程
|
||||
3.3.1 initrc配置文件
|
||||
3.3 内核启动
|
||||
|
||||
3.4 init进程启动
|
||||
3.4.1 initrc配置文件
|
||||
...
|
||||
3.3.x initrc添加xxx
|
||||
3.4 认识services
|
||||
3.5 认识framework
|
||||
3.6 认识libcore
|
||||
3.7 认识sepolicy
|
||||
3.8 认识linker
|
||||
3.4.x initrc添加xxx
|
||||
3.5 认识services
|
||||
3.6 认识framework
|
||||
3.7 认识libcore
|
||||
3.8 认识sepolicy
|
||||
3.9 认识linker
|
||||
|
||||
第4章 系统美化
|
||||
4.1 常见的系统美化方式
|
||||
|
Loading…
x
Reference in New Issue
Block a user