• Linux5.x启动过程分析


    Linux5.x启动过程 ARM Cotex_A7

    系统启动文件

    arch/arm/kernel/head.S
    
    • 1
    //Kernel startup code for all 32-bit CPUs
    //kernel启动执行的位置	
    Kernel startup entry point.
    .arm
    
    __HEAD
    ENTRY(stext)
     ARM_BE8(setend	be )			@ ensure we are in BE8 mode
    
     THUMB(	badr	r9, 1f		)	@ Kernel is always entered in ARM.
     THUMB(	bx	r9		)	@ If this is a Thumb-2 kernel,
     THUMB(	.thumb			)	@ switch to Thumb now.
     THUMB(1:			)
    
    
    bl	__lookup_processor_type
    
    bl	__create_page_tables
    
    
    ENTRY(__secondary_switched)
    ldr	sp, [r7, #12]			@ get secondary_data.stack
    mov	fp, #0
    b	secondary_start_kernel
    ENDPROC(__secondary_switched)
    
    #include "head-common.S"
    
    
    
    @第二段  C
    b secondary_start_kernel
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    source/kernel$ grep "secondary_start_kernel" * -nr
    
    • 1
    arch/arm/kernel/head-nommu.S:119:       b       secondary_start_kernel
    arch/arm/kernel/smp.c:382:      "       b       secondary_start_kernel"
    
    • 1
    • 2
    arch/arm/kernel/smp.c
    
    • 1
     asmlinkage void secondary_start_kernel(void)
    {
    	struct mm_struct *mm = &init_mm;
    	unsigned int cpu;
    
    	secondary_biglittle_init();
    
    cpu_init();
    
    local_irq_enable();
    local_fiq_enable();
    local_abt_enable();
    
    /*
     * OK, it's off to the idle thread for us
     */
    cpu_startup_entry(CPUHP_AP_ONLINE_IDLE);
    
    void cpu_startup_entry(enum cpuhp_state state)
    {
    	arch_cpu_idle_prepare();
    	cpuhp_online_idle(state);
    	while (1)
    		do_idle();
    }
    			https://blog.csdn.net/eidolon_foot/article/details/132575397
    		void cpu_startup_entry(enum cpuhp_state state)
    		{
    			arch_cpu_idle_prepare();
    			cpuhp_online_idle(state);
    			while (1)
    				do_idle();
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    这个cpu_startup_entry()函数是Linux内核启动最后一个关键步骤,它完成CPU的在线初始化。
    			主要功能:
    			1. 调用arch_cpu_idle_prepare(),进行CPU空闲状态下的架构相关初始化。
    			2. 调用cpuhp_online_idle(),通知CPU热插拔子系统,CPU进入在线空闲状态。
    			3. 进入死循环,反复调用do_idle()函数,让CPU进入空闲状态。
    			do_idle()是一个架构相关的函数,它会让CPU进入低功耗的空闲状态,并等待下个任务的调度。
    			在多核系统中,每个CPU的idle线程都会调用这个函数,进入空闲循环,等待调度新任务来运行。
    			至此,Linux内核启动过程全部完成,硬件和CPU已经初始化完毕,可以正式运行应用程序和服务了。cpu_startup_entry函数让CPU进入正常的调度循环,这是操作系统运行的典型状态。
    			这段代码定义了一个名为cpu_startup_entry的函数,它接受一个enum cpuhp_state类型的参数state。
    			 
    			函数的作用是在CPU启动时执行一些初始化操作。具体步骤如下:
    			 
    			调用arch_cpu_idle_prepare()函数进行CPU空闲状态的准备。
    			调用cpuhp_online_idle(state)函数将CPU设置为在线空闲状态,其中state参数指定了CPU的状态。
    			进入一个无限循环(while (1)),在循环中不断调用do_idle()函数执行CPU的空闲操作。
    			这段代码的目的是在系统启动时将CPU置于空闲状态,并进行一些初始化操作,以确保系统的正常运行。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    从kernel到init

    参考博文
    https://www.cnblogs.com/arnoldlu/p/10868354.html

    #include "head-common.S"
    arch/arm/kernel/head-common.S
    
    • 1
    • 2
    bl	__inflate_kernel_data		@ decompress .data to RAM
    bl	memcpy				@ copy .data to RAM
    bl	memset				@ clear .bss
    
    
    b	start_kernel
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    init/main.c
    
    • 1
    asmlinkage __visible void __init __no_sanitize_address start_kernel(void)
    {
    /* Do the rest non-__init'ed, we're now alive */
    	arch_call_rest_init();
    }
    
    
    void __init __weak arch_call_rest_init(void)
    {
    	rest_init();
    }
    
    	
    noinline void __ref rest_init(void)
    {
    	struct task_struct *tsk;
    	int pid;
    
    	pid = kernel_thread(kernel_init, NULL, CLONE_FS);
    	
    	cpu_startup_entry(CPUHP_ONLINE);
    	
    	pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
    	
    	
    	schedule_preempt_disabled();
    	/* Call into cpu_idle with preempt disabled */
    	cpu_startup_entry(CPUHP_ONLINE);
    	
    	static int __ref kernel_init(void *unused)
    	{
    	
    		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;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    kernel/kthread.c
    
    • 1
    int kthreadd(void *unused)
    {
    	 set_task_comm(tsk, "kthreadd");-------修改内核线程名为kthreadd。
    
    	内核线程的创建是由kthreadd遍历kthread_create_list列表,然后取出成员,通过create_kthread()创建内核线程。
    	while (!list_empty(&kthread_create_list)) {
     
    
    }	
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    pid-0是所有进程/线程的祖先,init负责所有用户空间进程创建,kthreadd是所有内核线程的祖先。

    busybox-1.27.2/init/init.c
    
    • 1
    /* Default sysinit script. */
    #ifndef INIT_SCRIPT
    # define INIT_SCRIPT  "/etc/init.d/rcS"
    #endif
    
    • 1
    • 2
    • 3
    • 4
    static void console_init(void)
    
    • 1
    int init_main(int argc UNUSED_PARAM, char **argv)
    
    • 1
    //{
    console_init();
    /* Make sure environs is set to something sane */设置环境变量,SHELL指向/bin/sh。
    putenv((char *) "HOME=/");
    putenv((char *) bb_PATH_root_path);
    putenv((char *) "SHELL=/bin/sh");
    putenv((char *) "USER=root"); /* needed? why? */
    //解析/etc/inittab文件,下面按照SYSINIT->WAIT->ONCE->RESPAWN|ASKFIRST顺序执行inittab内容。
    parse_inittab();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    }

  • 相关阅读:
    LLDB 三种输出方式 对比及原理探索
    jsp网络申报审批系统Myeclipse开发mysql数据库web结构java编程计算机网页项目
    072:vue+openlayers拖拽放大所选区域(DragPan示例代码)
    离子交换树脂在除氨氮领域的应用
    Flutter系列文章-Flutter基础
    卷积积分的计算
    Leetcode20.有效的括号
    2023年DevOps国际峰会暨BizDevOps企业峰会(DOIS北京站)-核心PPT资料下载
    基于FPGA的电子万年历设计
    C语言:用一级指针访问二维数组的max,min
  • 原文地址:https://blog.csdn.net/u010584870/article/details/134005081