• 2018-2019-1 20189210 《LInux内核原理与分析》第四周作业


    第三章
    这一章接触内核源代码,对内核源码进行编译和调试跟踪
    一、预备知识:
    内核:整个操作系统的最底层,它负责了整个硬件的驱动以及提供各种系统所需的内核功能。内核实质上是系统上面的一个文件而已,这个文件包含了驱动主机各项硬件的检测程序与驱动模块。当系统读完BIOS并加载MBR内的引导装载程序后,就能够加载内核到内存当中。然后内核开始检测硬件,挂载根目录并取得内核模块来驱动所有的硬件,之后调用/sbin/init就能依序启动多有系统所需要的服务了。
    Qemu :以GPL许可证分发源码的模拟处理器,能启动那些为不同中央处理器编译的Linux程序。

    二、构造一个简单的Linux内核
    构造的MenuOS系统是由Linux内核镜像和根文件系统集成起来的。
    Linux内核在PC上以文件的形式存在(保存成磁盘文件形式),就是所谓的“映像文件”。内核编译(make)之后会生成两个文件,一个Image,一个zImage,其中Image为内核映像文件,而zImage为内核的一种映像压缩文件,Image大约为4M,而zImage不到2M。为了能使用zImage这个压缩版本,必须在它的开头加上解压缩的代码,将zImage 解压缩之后才能执行,因此它的执行速度比Image要慢。但考虑到嵌入式系统的存储空容量一般都比较小,内核要常驻内存,采用zImage可以占用较少的存储空间,因此牺牲一点性能上的代价也是值得的,所以一般嵌入式系统均采用压缩的内核映像文件,即zImage。
    根文件系统首先是一种文件系统,该文件系统不仅具有普通文件系统的存储数据文件的功能,但是相对于普通的文件系统,它的特殊之处在于,它是内核启动时所挂载(mount)的第一个文件系统,内核代码的映像文件保存在根文件系统中,系统引导启动程序会在根文件系统挂载之后从中把一些初始化脚本(如rcS,inittab)和服务加载到内存中去运行。先将/dev/ram0挂载,而后执行/linuxrc.等其执行完后。切换根目录,再挂载具体的根文件系统.根文件系统执行完之后,也就是到了Start_kernel()函数的最后,执行init的进程,也就第一个用户进程。对系统进行各种初始化的操作。

    在实验楼中的虚拟机里,直接进入LinuxKernel目录使用如下命令就可以运行Linux内核源码和跟文件系统

    qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rotes.img

    其中bzImage是vmlinux经过gzip压缩后的文件,适用于大内核;vmLinux是编译出来的最原始的内核ELF文件;initrd是内存根文件系统,只创建了一个rootfs.img,其中只有一个init的功能,用menu程序替代init, 内核启动完成后进入menu程序。

    三、跟踪调试Linux内核的启动过程
    Qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rotes.img -s -S

    -S CPU初始化之前将其冻结起来

    -s 是默认在tcp::1234端口上创建了一个gdb-server

    再打开一个窗口,启动gdb,把内核加载进来,建立连接
    file linux-3.18.6/vmlinux #把带有符号表的内核镜像加载进来
    target remote:1234 #用tcp:1234端口来链接gdb server

    然后在start_kernel函数和rest_init处设置端点,调试运行

    四、对于start_kernel( )和rest_init( )的分析
    start_kernel( )相当于main.c中的main函数,是首先运行的,在此函数调用之前,内核代码主要是汇编语言编写的,用于完成硬件系统的初始化工作,为C代码的运行设置环境。这个函数主要进行各个模块初始化工作,trap_init()(中断向量的初始化)、mm_init()(内存管理的初始化)sched_init()(调度模块的初始化)等。
    init_task() :0号进程,初始化的起点
    struct task_struct init_task = INIT_TASK(init_task);
    可以看出 init_task(0号进程)是 task_struct 类型,是进程描述符,使用宏INIT_TASK对其进行初始化,并且init_task是唯一没有通过fork方式产生的进程。

    rest_init( ):
    在此函数中调用kernel_thread进程来创建kernel_init和kthreadd内核线程

    kernel_init()内核线程为1号内核线程,负责执行内核的部分初始化工作及进行系统配置,最后调用do_execve加载init程序,最后演变成用户态1号进程——init进程。
    kthreadd()函数的任务是管理和调度其他内核线程 kernel_thread。for 循环中运行 kthread_create_list 全局链表中维护的 kthread, 在create_kthread()函数中,会调用 kernel_thread 来生成一个新的进程并被加入到此链表中,因此所有的内核线程都是直接或者间接的以 kthreadd 为父进程。
    小结:
    本周学习了Linux内核源代码,对于内核的源代码目录,编译过程有了进一步的了解,但在学习中总会遇到新的问题,例如kernel_thread进程是由init零号进程fork出来的,以及idle进程与0号进程的关系,内核线程与用户进程的区别,这些问题在CSDN论坛中大部分都以解决,但发现内核内部的知识非常高深,还需多多查阅资料

  • 相关阅读:
    centos7 svn服务器的搭建
    fastdfs远程服务器java连接失败的问题
    Java实现视频网站的视频上传、视频转码、视频关键帧抽图, 及视频播放功能
    数据库访问辅助类SqlHelper
    实现基本的Ajax和Json请求
    在Asp.Net MVC中使用Repeater控件
    Asp.Net MVC向视图View传值的三种方法
    C#实现加简单的Http请求
    Http协议学习小结
    iOS开发之layoutSubviews
  • 原文地址:https://www.cnblogs.com/20189210mujian/p/9906672.html
Copyright © 2020-2023  润新知