当前位置: 首页 > 科技观察

手把手教你一步步分析Linux的启动过程

时间:2023-03-21 17:31:20 科技观察

下载Linux内核网站:https://www.kernel.org/最新的Linux内核是5.15版本。现在常用的Linux内核源码有4.14、4.19、4.9等版本,其中4.14版本的源码压缩包约90+M,解压后700+M,共61350个文件.这么多文件,用sourceinsight或者VSCode查看起来会比较困难,所以可以在线查看。在线查看Linux内核源码:https://elixir.bootlin.com/linux/latest/source在线查看Android源码:http://androidxref.com/Android系统是基于Linux内核,底层是Linux内核,源代码数量翻倍。因此,使用软件查看Android源代码难度较大,可以使用在线网站查看源代码。我们知道在Linux系统的启动前有一个bootloader,比如常用的uboot。本文不分析uboot的启动,只给出流程图:本文主要讲解从bootloader跳转到linux系统时的启动函数start_kernel。之后,这个函数初始化系统的进程。在linux4.14/arch/arm/kernel/head.S文件中,是最后组装阶段的初始化,然后会跳转到main.c文件的start_kernel函数,这里完成Linux的启动初始化,而这个函数会调用近100个函数来完成Linux系统的初始化。调用函数如下(不同内核版本,顺序和细节不同):linux4.14/init/main.c,start_kernel函数。asmlinkage__visiblevoid__initstart_kernel(void){char*command_line;char*after_dashes;set_task_stack_end_magic(&init_task);smp_setup_processor_id();debug_objects_early_init();cgroup_init_early();local_irq_disable();early_boot_irqs_disabled=true;/**Interrupts.Donestillemcess./boot_cpu_init();page_address_init();pr_notice("%s",linux_banner);setup_arch(&command_line);/**在arch之后设置初始金丝雀和熵*并在添加潜在和命令行熵之后。*/add_latent_entropy();add_device_randomness(command_line,strlen(command_line));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-specificboot-cpuhooks*/boot_cpu_hotplug_init();build_all_zonelists(NULL);page_alloc_init();pr_notice("Kernelcommandline:%s\n",boot_command_line);/*parametersmaysetstatickeys*/jump_label_init();parse_early_param();after_dashes=parse_args("启动内核",static_command_line,__start___param,__stop___param-__start___param,-1,-1,NULL,&unknown_bootoption);if(!IS_ERR_OR_NULL(after_dashes))parse_args("Settinginitargs",after_dashes,NULL,0,-1,-1,NULL,set_init_arg);/**这些使用largebootmemallocations并且必须先于*kmem_cache_init()*/setup_log_buf(0);pidhash_init();vfs_caches_init_early();sort_main_extable();trap_init();mm_init();ftrace_init();/*trace_printkcanbeenabledhere*/early_trace_init();/**在启动任何中断(例如*定时器中断)之前设置调度程序。完整的拓扑设置发生在smp_init()*time-butmeanwhilewestillhaveafunctioningscheduler.*/sched_init();/**Disablepreemption-earlybootupschedulingisextremely*frag_timer.disable*frag_timer.disable());if(WARN(!irqs_disabled(),"Interruptswereenabled*very*early,fixingit\n"))local_irq_disable();radix_tree_init();/**Allowworkqueuecreationandworkitemqueueing/cancelling*early.Workitemexecutiondependsonkthreadsandstartsafter*workqueue_init().*/workqueue_init_early();rcu_init();/*Traceeventsareavailableafterthis*/trace_init();context_tracking_init();/*initsomelinksbeforeinit_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_safe_init();perf_event_init();profile_init();call_function_init();WARN(!irqs_disabled(),"Interruptswereenabledearly\n");early_boot_irqs_disabled=false;local_irq_enable();kmem_cache_init_late();/**HACKALERT!这很早。我们在*我们完成PCI设置等之前启用控制台,console_init()必须意识到*这一点。*/console_init();if(panic_later)panic("Toomanyboot%svarsat`%s'",panic_later,panic_param);lockdep_info();/**需要在启用irqs时运行这个,因为它想要*自我测试[硬/软]-irqson/offlockinversionbugs*too:*/locking_selftest();/**ThisneedstobecalledbeforeanydevicesperformDMA*operationsthatmightusetheSWIOTLBbouncebuffers.Itwill*markthebouncebuffersasdecryptedsothattheirusagewill*notcause"plain-text"datatobedecryptedwhenaccessed.*/mem_encrypt_init();#ifdefCONFIG_BLK_DEV_INITRDif(initrd_start&&!initrd_below_start_ok&&page_to_pfn(virt_to_page((void*)initrd_start))