• 2019-2020-1 20199311《Linux内核原理与分析》第四周作业


    1. 问题描述

    通过这一周的学习,我们进一步学习了linux内核源代码的目录结构,并基于linux内核源代码构造了一个简单的操作系统MenuOS,同时通过分析MenuOS的启动过程,使用gdb跟踪调试内核从start_kernel到init进程启动,并分析linux内核启动部分函数的代码,进一步深入了解linux内核的工作原理。
    2. 解决步骤

    2.1 linux内核源码介绍

    Linux内核源码的目录结构如下图所示
    图片描述
    下面介绍部分比较重要的目录

    • arch:arch是architecture的缩写。内核所支持的每种CPU体系,在该目录下都有对应的子目录。每个CPU的子目录,又进一步分解为boot,mm,kernel等子目录,分别包含控制系统引导,内存管理,系统调用等。

    • block:部分块设备驱动程序。

    • crypto:加密、压缩、CRC校验算法。

    • documentation:存放内核的一些文档。

    • drivers:驱动目录,里面分门别类的存放了Linux内核支持的所有硬件设备的驱动源代码。

    • fs:存放各种文件系统的实现代码。每个子目录对应一种文件系统的实现。

    • include:内核所需要的头文件,存放公共的(各种CPU体系结构公用的)头文件。

    • init:内核初始化代码。

    • ipc:进程间通信的实现代码。

    • kernel:linux大多数关键的核心功能都是在这个目录实现。

    • lib:库文件代码。

    • mm:mm目录中的文件用于实现内存管理中与体系结构无关的部分。

    • net:网络协议的实现代码。

    • samples:一些内核编程的范例。

    • scripts:配置内核的脚本。

    2.2 构造一个简单的Linux内核

    这里采用“实验楼”环境,实验楼环境的linux内核版本为3.18.6。使用如下命令构建一个linux内核。

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

    qemu仿真kernel;bzImage是vmLinux经过gzip压缩后的文件,是压缩的内核映像,“b”代表的是“big”(bzImage适用于大内核,zImage适用于小内核)。根文件系统一般包括内存根文件系统和磁盘文件系统。这里的根文件系统也比较简单,只是创建了一个rootfs.img。

    2.3 使用gdb跟踪调试

    使用gdb跟踪调试内核,加两个参数,一个是-s(在1234端口上创建了一个gdb-server,可以打开另一个窗口,用gdb把带有符合表的内核镜像加载进来,然后连接gdb server,设置断点跟踪内核。),另一个是-S(CPU初始之前就冻结起来)。
    内核启动效果如下
    图片描述
    打开一个窗口,启动gdb,把内核加载进来,建立连接
    使用如下命令

    #加载符号表
    file linux-3.18.6/vmlinux
    #用1234这个端口进行连接
    target remote:1234
    

    图片描述
    在start_kernel处设置断点,刚才是stop状态,如果按“c”继续执行,那么系统开始启动执行,启动到start_kernel函数的位置停在断点处
    图片描述
    再设置一个断点rest_init,继续执行,停在断点处。
    图片描述

    2.4 分析内核启动过程中start_kernel函数作用

    init目录中的main.cc源文件是整个linux内核启动的起点,但它的起点不是main函数,而是start_kernel函数,在start_kernel函数中Linux将完成整个系统的内核初始化。内核初始化的最后一步rest_init函数就是启动init进程这个所有进程的祖先。

    2.4.1 start_kernel函数

    首先执行start_kernel函数,完成一系列的模块、时钟、进程等初始化工作。
    图片描述

    asmlinkage __visible void __init start_kernel(void)
    {
    …
    set_task_stack_end_magic(&init_task);
    printk(KERN_NOTICE"%s", linux_banner);  /* 输出linux版本信息 */
    setup_arch(&command_line);   /* 设置与初始化硬件体系相关的环境并调用 */
    sched_init()                 /* 初始化调度器,先于中断开始前 */
    printk(boot_command_line);   /* 提取分析核心启动参数过程(从bootloader中传递) */
    parse_early_param();
    parse_args
    trap_init();                  
    return
    early_irq_init();              /* 中断初始化过程 */
    init_IRQ();          
    init_timers();                /* 初始化定时器 */
    timekeeping_init(); 
    time_init(); /* 设置定时器及返回当前时间 */
    console_init() /* 初步的初始化控制台 */
    vmalloc_init();
    vfs_caches_init_early(); 
    mem_init(); /* 初始化内存并计算可用内存大小 */
    kmem_cache_init(); /* 初始化SLAB缓存分配器 */
    calibrate_delay(); /* 延迟校准 */
    fork_init(num_physpages); /* 初始化max_threads,init_task参数为fork()提供参考 */
    buffer_init(); /* 初始化块设备读写缓冲区 */
    vfs_caches_init(num_physpages); 
    signals_init(); /* 初始化内核信号队列 */
    rest_init(); /* 最后实际进入reset_init()函数,包括所有剩下的硬件驱动,线程初始化等过程,这也最终完成start_kernel的启动过程 */
    }
    

    2.4.2 rest_init函数

    通过rest_init函数新建kernel_init和kthreadd内核进程
    图片描述

    kernel_thread(kernel_init, NULL, CLONE_FS); #创建一个内核线程,实际上是一个内核进程,其中pid=1。其中kernel_init只是一个函数
    pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
    #在kthreadd函数中kthread_create_list全局链表中维护的内核线程。当调用kthread_create时,会创建一个kthread,并被添加到kthread_create_list链表中。当进程执行完毕后,就会被从链表中删除。
    

    3.总结

    通过这周的学习,我初步了解了linux内核启动过程的大概步骤,在接下来的学习中,将更进一步学习linux系统内核的更多功能。

  • 相关阅读:
    MySQL 一般模糊查询的几种用法
    MySQL插入中文数据报错
    BeanUtils.populate 的作用
    分分钟搞定 JSP 技术
    margin-top相对谁的问题
    常用汉字的Unicode码表
    从InputStream到String_写成函数
    Http请求和响应应用
    发布mvc报错:403.14-Forbidden Web 服务器被配置为不列出此目录的内容
    导出到excel
  • 原文地址:https://www.cnblogs.com/w-a-n-s-d-j/p/11653425.html
Copyright © 2020-2023  润新知