zoukankan      html  css  js  c++  java
  • Linux系统的启动过程

    陈民禾原创作品转载请注明出处《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ”,博客内容的具体要求如下:

    一.上一周内容复习

        上一周主要学习了计算机的三大法宝:存储程序计算机、函数调用堆栈、中断。

        操作系统有两把宝剑:中断上下文的切换(也就是保存现场和恢复现场)和进程上下文的切换

    二.Linux内核源代码简介

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

       

       首先在最左边一列是arch/目录,arch目录是代码量相当庞大,因为它的内核支持很多体系结构,这里面的内容很复杂。arch/x86目录下的代码时我们重点关注的。我们可以在再回到根目录。根目录除了arch目录还有几个其他的目录,比如Documentation文档,drivers也有很多源代码,还有firmware,还有include,还有另外一个很关键的目录——init目录,init目录下一个很关键的目录是main.c,内核启动的相关的代码都在init目录下,我们在阅读普通的代码的时候都有一个main函数,我们从main函数开始读代码,同样呢,linux在init目录下有个main.c,这是整个Linux内核启动起点,起点不是main函数,是start_kernel,start_kernel函数相当于普通C程序的的main 函数,这个实际上是初始化linux内核的起点,这个地方是内核开始初始化了。

        我们再详细分析,我们再回过头来看net,那么我们比较关心的是arch下面的X86,还有init、kernel的代码,总之这个从软件工程来看是实现视图里面内核源代码的readme.readme里面有很多的内容,很多配置方法,执行make运行。make all noconfig是把所有的可选项目都给关闭了,ps:编译内核是make all。

    三.下面是直接用自己的笔记本电脑虚拟机运行的方法

    首先可以下载内核源代码编译内核

    cd ~/LinuxKernel/
    wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.18.6.tar.xz
    xz -d linux-3.18.6.tar.xz
    tar -xvf linux-3.18.6.tar
    cd linux-3.18.6
    make i386_defconfig
    make#一般要编译很长时间,少则20分钟多则数小时

    制作根文件系统

    cd ~/LinuxKernel/
    
    git clone https://github.com/menging/menu.git
    
    cd menu
    
    gcc -o init linktable.c menu.c test.c -m32 -static -lpthread    /*用一个gcc来简单编译一下,静态的,32位的编译成的文件名叫init,init是第一个用户态进程,是1号进程*/
    
    cd ../rootfs  
    
    cp ../menu/init ./         //把init拷贝到rootfs的目录下面去
    
    find . | cpio -o Hnewc | gzip -9> ../rootfs.img   //使用cpio这个方法把所有的文件打包成一个rootfs的镜像文件,这样根文件系统的镜像就制作好了

     启动MenuOS系统

    cd ~/LinuxKernel/
    
    qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img
    1.在原来配置的基础上,make menuconfig选中如下选项重新配置Linux,使之携带调试信息
    
    kernel hacking->
    
    2.[*]compile the kernel with debug info //不用实验楼,需要做这一步
    
    3.make重新编译,时间较长

    使用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

    打开另一个shell窗口

    gdb
    
    (gdb) file linux-3.18.6/vmlinux #在gdb界面中targe remote之前加载符号表
    
    (gdb) target remote:1234# 建立gdb和gdbsever之间的连接,按c让qemu
    
    (gdb)break start_kernel#断点的设置可以在target remote之前,也可以在之后

    下面就是在实验楼里面这么把我们构造的一个简单的linux_kernel运行起来,这个内核源代码已经给编译好了,这里边还有一个rootfs,这里面有一个rootfs。img,我们前面的工作已经做好了,所以这里我们可以直接启动起来 qemu -kernel linux -3.18.6/arch/x86/boot/bzImage -initrd也就是指明一个根文件系统,根文件系统就是rootfs.img我们就这样将其启动起来,接下来呢,我们可以看到内核在启动,内核启动完了就开始执行了,可以在图中看到MenuOS的字样。

    四.内核部分代码分析

    里面的主要程序及功能:
    setup_arch(&command_line) ,完成内存映像的初始化; 
    page_alloc_init(),创建内核页表,映射所有物理内存和io空间;
    trap_init(),初始化硬件中断,函数中设置了很多中断门; 
    sched_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内核,加载出MenuOS

    进入内核文件目录使用qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S命令启动内核,-S选项表示在CPU初始化时冻结内核。

    在终端分割出一个窗口用于gdb调试

    gdb break

    file linux-3.18.6/vmlinux # 在gdb界面中targe remote之前加载符号表

    target remote:1234 # 建立gdb和gdbserver之间的连接,按c 让qemu上的Linux继续运行

    break start_kernel # 断点的设置可以在target remote之前,也可以在之后

    使用break命令在start_kernel处设置断点,按c继续执行内核到断点位置

    此时使用list命令可以看到start_kernel位置的代码

     

     跟踪cpu_startup_entry断点,0号进程启动

     五.实验感想

         内核的启动过程可以简单地这么来看:start_kernel从内核一启动的时候它会一直存在,这个就是0号进程,idle就是一个while0,一直在循环着,当系统没有进程需要执行的时候就调度到idle进程,我们在windows系统上会经常见到,叫做system idle,这是一个一直会存在的0号进程,然后呢就是0号进程创建了1号进程,这个init_process是我们的1号进程也就是第一个用户态进程,也就是它默认的就是根目录下的程序,也就是常会找默认路径下的程序来作为1号进程,1号进程接下来还创建了kthreadd来管理内核的一些线程,这样整个程序就启动起来了。也就是所谓的道生一,一生二,二生三,三生万物。 

  • 相关阅读:
    UVA 10600 ACM Contest and Blackout(次小生成树)
    UVA 10369
    UVA Live 6437 Power Plant 最小生成树
    UVA 1151 Buy or Build MST(最小生成树)
    UVA 1395 Slim Span 最小生成树
    POJ 1679 The Unique MST 次小生成树
    POJ 1789 Truck History 最小生成树
    POJ 1258 Agri-Net 最小生成树
    ubuntu 用法
    ubuntu 搭建ftp服务器,可以通过浏览器访问,filezilla上传文件等功能
  • 原文地址:https://www.cnblogs.com/20135124freedom/p/5273033.html
Copyright © 2011-2022 走看看