• Linux内核启动过程的分析


    Linux内核启动过程分析

    Linux内核启动过程分析


    这次我们使用gdb跟踪Linux内核的启动来分析其启动过程,内核版本3.18.6

    Linux内核的启动从src/init/main.c的start_kernel函数开始,因此使用gdb在start_kernel函数下断点并进行跟踪

    start_kernel的代码比较多,近200行,大多数是各个系统模块的初始化,本文不叙述这部分内容,本文需要注意的代码位于start_kernel函数的开头部分和结尾部分,粘贴如下

    asmlinkage __visible void __init start_kernel(void)
    {
        char *command_line;
        char *after_dashes;
    
        /*
         * Need to run as early as possible, to initialize the
         * lockdep hash:
         */
        lockdep_init();
        set_task_stack_end_magic(&init_task);
        
        ........
        
        /* Do the rest non-__init'ed, we're now alive */
        rest_init();
    }

    set_task_stack_end_magic(&init_task);

    这句是内核启动的第一步,init_task是一个声明在init_task.c中的全局变量,具体定义位于init_task.h,其内容就是一个完整的进程控制块,这句中的函数名可以推测出其作用,即标记处进程栈的底部,其具体实现位于fork.c和sched.c中,sched.c中实现的是设置进程栈底的功能

    static inline unsigned long *end_of_stack(struct task_struct *p)
    {
    #ifdef CONFIG_STACK_GROWSUP
        return (unsigned long *)((unsigned long)task_thread_info(p) + THREAD_SIZE) - 1;
    #else
        return (unsigned long *)(task_thread_info(p) + 1);
    #endif
    }

    到这儿就不难理解了。init_task这个手动设置出来的进程控制块实际上就是0号进程,也就是Linux后续系统进程和用户进程的起点

    设置完0号进程以后,start_kernel执行了大量运行环境的初始化,包括中断门初始化,SMP结构初始化,内存分页初始化,终端初始化等等,到最后一行的rest_init,在rest_init中,Linux内核将启动第一个用户态进程,即init进程

    static noinline void __init_refok rest_init(void)
    {
        int pid;
        rcu_scheduler_starting();
    
        kernel_thread(kernel_init, NULL, CLONE_FS);
        numa_default_policy();
        pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
        rcu_read_lock();
        kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
        rcu_read_unlock();
        complete(&kthreadd_done);
    
        init_idle_bootup_task(current);
        schedule_preempt_disabled();
        /* Call into cpu_idle with preempt disabled */
        cpu_startup_entry(CPUHP_ONLINE);
    }

    rest_init首先初始化了调度器,随后以kernel_init作为处理函数构造出了一个内核线程,接着在启动调度器进行一次调度,在代码注释里面说的很清楚

        /*
         * We need to spawn init first so that it obtains pid 1, however
         * the init task will end up wanting to create kthreads, which, if
         * we schedule it before we create kthreadd, will OOPS.
         */

    而cpu_startup_entry这个函数最终会进入cpu_idle_loop();这样一个死循环,

    kernel_init也就是1号init进程,即一切用户态进程的祖先

    static int __ref kernel_init(void *unused)
    {
        ......
        if (ramdisk_execute_command) {
            ret = run_init_process(ramdisk_execute_command);
            if (!ret)
                return 0;
            pr_err("Failed to execute %s (error %d)
    ",
                   ramdisk_execute_command, ret);
        }
    
        if (execute_command) {
            ret = run_init_process(execute_command);
            if (!ret)
                return 0;
            pr_err("Failed to execute %s (error %d).  Attempting defaults...
    ",
                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.");
    }

    可以看出kernel_init和linux的initrd机制有关联,最后按照/sbin/init/;/etc/init/;/bin/init/;/bin/sh;的顺序寻找init执行,我们之前的menuOS就是通过initrd来执行的,可以将menuos换成其他C代码作为init执行

    吴韬 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

  • 相关阅读:
    [转载]#2002 服务器没有响应 (或者本地 MySQL 服务器的套接字没有正确配置
    [转载]使用Cufon技术实现Web自定义字体
    [转载]Fedora14下MySQL、Apache、PHP、phpMyAdmin的安装步聚
    GUID的使用
    访问来源记录
    MongoDB数据库的基本概念
    域登录验证
    sql语法和MongoDB语法的对应关系
    ADO.NET数据集添加虚拟字段
    MongoDB开发常用资源地址
  • 原文地址:https://www.cnblogs.com/current/p/4357885.html
Copyright © 2020-2023  润新知