Linux内核代码分析之start_kernel

江纯杰 原创作品转载请注明出处 
《Linux内核分析》MOOC课程

前面我们学习一些函数调用堆栈和进程切换的一些知识,现在开始来接触linux的内核代码。
Linux内核代码很庞大,不可能完全吃下它,我们只能选取一些核心的我们关心的来解读。

现在,我们先来看看Linux内核的目录结构,选取比较新的 Linux-3.18.6 这个版本来看一看。

Linux内核代码目录结构

  • arch : 针对不同的计算机体系结构
  • block : 块设备驱动
  • crypto
  • documentation : 内核文档
  • drivers : 设备驱动
  • fs : 文件系统
  • include : 头文件
  • init : 初始化
  • ipc : 进程间通信
  • kernel : Linux大多数关键的核心功能都在此目录
  • lib : 库
  • mm : 内存管理
  • net : 网络协议
  • samples :
  • scripts: 配置内核的脚本
  • security :
  • sound : 音频设备驱动
  • usr :
  • virt :

启动内核

使用实验楼环境,内核启动完成后进入menu程序(《软件工程C编码实践篇》的课程项目),支持三个命令help、version和quit。截图如下

Linux内核代码分析之start_kernel_第1张图片

gdb跟踪调试

我们主要关心的是start_kernel的启动过程,使用gdb在关键位置设置断点观察。

qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S 
# -S freeze CPU at startup (use ’c’ to start execution)
# -s shorthand for -gdb tcp::1234 若不想使用1234端口,则可以使用-gdb tcp:xxxx来取代-s选项

Linux内核代码分析之start_kernel_第2张图片

分析代码

start_kernel 的详细代码,可以看到,里面调用了很多函数,这些都是重量级函数,如
setup_arch(&command_line) ,完成内存映像的初始化;

page_alloc_init(),创建内核页表,映射所有物理内存和io空间;

trap_init(),初始化硬件中断,函数中设置了很多中断门;

sched_init(),任务调度初始化。

还有很多重要初始化工作都在这里进行,就不一一列举了。

我们来看看start_kernel里面最后一个调用的函数rest_init()。

static noinline void __init_refok rest_init(void)
{
    int pid;

    rcu_scheduler_starting();
    /*
     * 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.
     */
    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);

    /*
     * The boot idle thread must execute schedule()
     * at least once to get things moving:
     */
    init_idle_bootup_task(current);
    schedule_preempt_disabled();
    /* Call into cpu_idle with preempt disabled */
    cpu_startup_entry(CPUHP_ONLINE);
}

kernel_thread()创建了一个线程,其参数kernel_init是一个函数,可以看到这个函数末尾的代码

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;

执行/sbin/init程序。init进程是Linux系统的1号进程,由Linux内核直接启动,是其他用户进程的祖先。
然后新建kthread进程,即2号进程,是内核态进程的祖先。

总结

linux内核很复杂,这里只是分析从start_kernel到init,来理解linux的第一个用户进程的产生过程。然而这只是九牛一毛,还需要做很多很多的工作才能更深入的了解操作系统的内核。

你可能感兴趣的:(linux,kernel,kernel,结构,代码分析)