Android之init启动流程(一)

来源:互联网 发布:在线直播软件 编辑:程序博客网 时间:2024/05/01 13:53

本文着重分析android-init启动流程

该进程为系统从内核空间启动的进程,是用户空间的鼻祖级进程。在此,谨分析两个层面:

一,kernel启动init

二,init启动流程

首先,kernel启动init
在bootloader的启动末段(经过了kernel解压缩和汇编执行启动阶段),会通过汇编直接调用init/main.c中start_kernel()接口,开始进入kernel部分执行,如此,就从此函数分析起:


asmlinkage void __init start_kernel(void)
{
char * command_line;
extern const struct kernel_param __start___param[], __stop___param[];

1,smp_setup_processor_id();
指定当前的cpu的逻辑号,这个函数对应于对称多处理器的设置,当系统中只有一个cpu的情况,此函数为空,什么也不做。

2,lockdep_init();
初始化内核依赖的关系表。一些体系结构拥有自己的start_kernel()代码回去调用lockdep_init(),与此同时也会从start_kernel()中调用lockdep_init()仅仅是为了初始化hash表。

3,local_irq_disable();
关闭当前CPU中断。

4,tick_init();
如果没有定义   CONFIG_GENERIC_CLOCKEVENTS宏定义,则这个函数为空函数,如果定义了这个宏,这执行初始化 tick控制功能,注册clockevents的框架。

5,boot_cpu_init();
如果是多核处理器,则设置第一号CPU为活跃CPU,如果是单核,则设置CPU为活跃CPU。

6,page_address_init();
当定义了CONFIG_HIGHMEM 宏,并且没有定义   WANT_PAGE_VIRTUAL 宏时,非空函数。其他情况为空函数。注意:ARM9不支持高端地址(大于896M),一般的嵌入式产品也不会用高端地址,所以,在ARM体系结构下,此函数为空!

7,setup_arch(&command_line);

该函数在所在的路劲为 /arm/kernel/setup.c,其中那个command_line就是有bootloader传过来的!这个函数是个重量级的函数,大家不可忽视!该函数完成体系结构相关的初始化,内核移植的过程一般也就到此函数为止了,其余的就只是一些相关的外设驱动。


8,setup_command_line(command_line);
保存未改变的comand_line到字符数组static_command_line[] 中。保存  boot_command_line到字符数组saved_command_line[]中。

setup_nr_cpu_ids();

9,setup_per_cpu_areas();
如果没有定义CONFIG_SMP宏,则这个函数为空函数。如果定义了CONFIG_SMP宏,则这个setup_per_cpu_areas()函数给每个CPU分配内存,并拷贝.data.percpu段的数据。为系统中的每个CPUper_cpu变量申请空间。

smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */

10,build_all_zonelists(NULL);
建立系统内存页区链表。

11,page_alloc_init();
这个函数会调用hotcpu_notifier函数。当然,在编译选项CONFIG_HOTPLUG_CPU起作用时,这个函数才有效。这个编译选项就是性感的热插拔技术,而且是CPU的热插拔。如果定义了,就去新建一个notifier_block结构,并调用register_cpu_notifier函数,将新建的这个结构挂到全局cpu_chain链中,具体的代码我就不去分析了。

12,parse_early_param();
boot_command_line进行解析。

setup_log_buf(0);
pidhash_init();

13,vfs_caches_init_early();
文件系统早期初始化,主要负责dentry和inode的hashtable的初始化工作。

14,sort_main_extable();
异常处理函数表排序。

15,trap_init();
重新设置中断向量表。

16,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.
*/
17,,sched_init();
核心进程调度器初始化,调度器的初始化的优先级要高于任何中断的建立,并且初始化进程0,即idle进程,但是并没有设置idle进程的NEED_RESCHED标志,所以还会继续完成内核初始化剩下的事情。

18,preempt_disable();
进制内核的抢占。使当前进程的   struct thread_info结构  preempt_count成员的值增加1

if (!irqs_disabled()) {
printk(KERN_WARNING "start_kernel(): bug: interrupts were "
"enabled *very* early, fixing it\n");
local_irq_disable();
}

19,idr_init_cache();
这个函数是创建IDR机制的内存缓存对象。所谓的IDR就是整数标识管理机制(integerIDmanagement)。引入的主要原因是管理整数的ID与对象的指针的关系,由于这个ID可以达到32位,也就是说,如果使用线性数组来管理,那么分配的内存太大了;如果使用线性表来管理,又效率太低了,所以就引用IDR管理机制来实现这个需求。

20,perf_event_init();


21,rcu_init();
初始化RCU(Read-Copy Update),主要是一个per_cpu_rcu_tasklet 。

22,radix_tree_init();
 radix_tree初始化,该功能主要加速look up dirty or writeback pages。这个函数是初始化radix树,radix树基于二进制键值的查找树。

23,early_irq_init();
中断初始化过程。

24,init_IRQ();
初始化IRQ中断和终端描述符。初始化系统中支持的最大可能的中断描述结构struct irqdesc变量数组irq_desc[NR_IRQS],把每个结构变量irq_desc[n]都初始化为预先定义好的坏中断描述结构变量bad_irq_desc,并初始化该中断的链表表头成员结构变量pend。

25,prio_tree_init();
这个函数是初始化优先搜索树,主要用在内存反向搜索方面。

26,init_timers();
初始化定时器Timer相关的数据结构。

27,hrtimers_init();
对高精度时钟进行初始化。

28,softirq_init();
初始化软中断。

29,timekeeping_init();
初始化资源和普通计时器。

30,time_init();
初始化系统时间,检查系统定时器描述结构struct sys_timer全局变量system_timer是否为空,如果为空将其指向dummy_gettimeoffset()函数。

31,profile_init();
对内核的一个性能测试工具profile进行初始化。

call_function_init();

if (!irqs_disabled())

printk(KERN_CRIT "start_kernel(): bug: interrupts were "
"enabled early\n");
early_boot_irqs_disabled = false;

32,local_irq_enable();
使能irq。

33,kmem_cache_init_late();
初始化cache。

34,console_init();
初始化控制台以显示printk的内容,在此之前调用的printk  ,只是把数据存到缓冲区里,只有在这个函数调用后,才会在控制台打印出内容!该函数执行后可调用printk()函数将log_buf中符合打印级别要求的系统信息打印到控制台上。

if (panic_later)

panic(panic_later, panic_param);

35,lockdep_info();
如果定义了CONFIG_LOCKDEP宏,那么就打印锁依赖信息,否则什么也不做。

/*
* Need to run this when irqs are enabled, because it wants
* to self-test [hard/soft]-irqs on/off lock inversion bugs
* too:
*/
36,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) {
printk(KERN_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

37,page_cgroup_init();
容器组的页面内存分配。

38,enable_debug_pagealloc();

设置内存分配是否需要输出调试信息,如果调用这个函数,当分配内存时,不会输出一些相关的信息。

39,debug_objects_mem_init();
创建调试对象内存缓存。

40,kmemleak_init();
<span style="font-family:Microsoft YaHei;">kmemleak初始化,Kmemleak工作于内核态,Kmemleak 提供了一种可选的内核泄漏检测,其方法类似于跟踪内存收集器。当独立的对象没有被释放时,其报告记录在 /sys/kernel/debug/kmemleak中,Kmemcheck能够帮助定位大多数内存错误的上下文。</span>

41,setup_per_cpu_pageset();

初始化非统一内存访问系统中的内存的策略,一般用于多处理器系统,在我们这里为空。

42,numa_policy_init();
初始化NUMA的内存访问策略。

if (late_time_init)

43,late_time_init();
运行时钟相关后期的初始化功能。

44,sched_clock_init();
进程调度时钟初始化。

45,calibrate_delay();

校验延时函数的精确度。

46,pidmap_init();
进程号位图初始化,一般用一个page来只是所有的进程PID占用情况。

47,anon_vma_init();

初始化反向映射的匿名内存,提供反向查找内存的结构指针位置,快速地回收内存。

#ifdef CONFIG_X86
if (efi_enabled)
efi_enter_virtual_mode();
#endif

48,thread_info_cache_init();
线程信息的缓存初始化。

cred_init();

49,fork_init(totalram_pages);
初始化fork()的工作环境。

50,proc_caches_init();
为proc文件系统创建高速缓存。

buffer_init();

51,key_init();
没键盘为空,有键盘初始化一个高速缓存。

security_init();

dbg_late_init();


52,vfs_caches_init(totalram_pages);
虚拟文件系统挂载。

53,signals_init();
初始化信号量。

/* rootfs populating might need page-writeback */

54,page_writeback_init();
CPU在内存中开辟高速缓存,CPU直接访问高速缓存提以高速度。当cpu更新了高速缓存的数据后,需要定期将高速缓存的数据写回到存储介质中,比如磁盘和flash等。这个函数初始化写回的周期。

#ifdef CONFIG_PROC_FS

55,proc_root_init();
如果配置了proc文件系统,则需初始化并加载proc文件系统。在根目录的proc文件夹就是proc文件系统,这个文件系统是ram类型的,记录系统的临时数据,系统关机后不会写回到flash中。
#endif

cgroup_init();

cpuset_init();

56,taskstats_init_early();
进程状态初始化,实际上就是分配了一个存储线程状态的高速缓存。

delayacct_init();

57,check_bugs();
测试CPU的缺陷,记录检测的缺陷,便于内核其他部分工作的需要。

acpi_early_init(); /* before LAPIC and SMP init */
sfi_init_late();

ftrace_init();

/* Do the rest non-__init'ed, we're now alive */
58,rest_init();
创建init进程。
}


。。。。。。start_kernel函数终于告一段落了,其最后调用了rest_init()函数,该函数创建了kernel_init内核线程。

rest_init()->kernel_init()->init_post()->run_init_process(ramdisk_execute_command)执行android文件系统根目录init文件。

下一篇继续init。



















0 0
原创粉丝点击