zoukankan      html  css  js  c++  java
  • 从linux内核代码分析操作系统启动过程

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

          在本次的实验中,我们将通过简单分析Linux内核代码来探讨操作系统的启动过程。

          计算机启动的过程其实在Andrew S.Tanenbaum所著的《现代操作系统》(中文版第18页)中就有大略的描述:

          1.计算机启动时,存储在RAM中的BIOS程序检查计算机的所有设备,包括RAM、键盘、鼠标、ISA及PCI总线上的设备等,这些设备将被记录下来。

          2.BIOS寻找可引导介质,从软盘、CD或硬盘中加载操作系统,将控制权交给操作系统中的引导程序。

          3.操作系统询问BIOS,获得各个设备的配置信息,然后开始进行初始化工作。

          而我们今天主要是分析linux内核在进行初始化工作时的步骤,首先是实验代码和截图:

          进入实验楼的系统中,首先进入linux内核文件,并启动内核镜像:

    cd LinuxKernel/
    qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S

      

         然后另开一个shell来利用gdb调试内核代码:

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

      

         

          通过输入"c"来启动内核初始化,当执行到break设定的函数时会自动停止。如图所示。

          下面我们来对内核初始化过程进行简单分析。

          内核的初始化过程由start_kernel函数开始,至第一个用户进程init结束,调用了一系列的初始化函数对所有的内核组件进行初始化。其中,start_kernel、rest_init、kernel_init、init_post等4个函数构成了整个初始化过程的主线。如下图所示:

          

           首先我们分析start_kernel:

           

          注意第510行,set_task_stack_end_magic设置了第0号进程的进程管理块,也就是init_task。它是一个比较特殊的进程,其他进程一般要通过fork命令来完成,只有它是由内核开发者人为制造出来的,它的初始化由arch/x86/kernel/init_task.c中的代码来完成。

          接下来则进行了一系列的初始化工作,直到执行到最后一行,进入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);
    }
    

      在rest_init中,系统开始真正地产生进程。第11行代码“kernel_thread(kernel_init, NULL, CLONE_FS);”就是具体的创建进程的语句。

          kernel_init函数将完成设备驱动程序的初始化,并调用init_post函数启动用户空间的init进程。

         而此时,我们第一个创建的进程init_task已经完成了自己的使命,它被start_kernel函数中的shed_init()函数转化为一个idle task,当运行队列中没有别的进程时,它便会进入不断的循环,直到运行队列中加入新的进程时才将控制权切换到新进程上。由此内核就启动完毕了。

       

          参考资料

          《现代操作系统》 Andrew S. Tanenbaum 机械工业出版社

          http://book.51cto.com/art/201007/213598.htm

          Linux内核中的init_task进程和idle进程

  • 相关阅读:
    mydumper 备份工具
    k8s 二进制部署详解
    etcd+https部署
    Harbor + Https 部署
    Nginx+Tomcat+Memcache 实现session共享
    rap 部署
    Centos6.5下rsync+inotify的配置详解
    python2.7中MySQLdb的安装与使用详解
    Linux中普通用户提权为超级用户
    Mysql忘记密码
  • 原文地址:https://www.cnblogs.com/wickedpriest/p/4356593.html
Copyright © 2011-2022 走看看