zoukankan      html  css  js  c++  java
  • 2020-2021-1 20209308《Linux内核原理与分析》第四周作业

    Linux 基础

    实验三

    一.实验过程

    1.内核源代码编译

    	mkdir LinuxKernel
    	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_defcongig
    	make
    
    

    2.制作根文件系统

    mkdir rootfs
    git clone https://github.com/mengning/menu.git
    cd menu
    gcc -pthread -o init linktable.c menu.c test.c -m32 -static
    cd ../rootfs
    cp ../menu/init ./
    find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img
    

    gdb跟踪调试内核启动过程

    ①启动内核:

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

    关于-s和-S选项的说明:

    • -S freeze CPU at startup (use ’c’ to start execution) 在系统启动的时候冻结CPU,使用c键继续执行后续操作
      -s shorthand for -gdb tcp::1234 打开远程调试端口,默认使用tcp协议1234端口,若不想使用1234端口,则可以使用-gdb tcp:xxxx来取代-s选项。
      指令的作用是在开始的时候就让CPU停止在启动的那一刻,我们可以看到如下的界面:

    接着进入gdb:

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

    断点源代码如图:


    3.添加指令

    //在test.c中添加hello函数并在main函数中添加相应的menuconfig
    int hello(int argc, char *argv[]){
    	printf("hello20209308
    ");
    	return 0;
    }
    
    int main()
    {
        PrintMenuOS();
        SetPrompt("MenuOS>>");
        MenuConfig("hello","myhello",hello);
        ExecuteMenu();
    }
    

    更改代码后重新执行

    make
    gcc -pthread -o init linktable.c menu.c test.c -m32 -static
    cd ../rootfs
    cp ../menu/init ./
    find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img
    

    将代码的变化更新到rootfs.img镜像中,这样在qemu中才会有新指令。


    二.实验代码分析

    1.0号进程的创建

    asmlinkage __visible void __init start_kernel(void)
    {
        //命令行,存放bootloader传递过来的参数
        char *command_line;
        char *after_dashes;
    
        /*
         * Need to run as early as possible, to initialize the
         * lockdep hash:
         */
        lockdep_init();    //初始化内核调试模块
        set_task_stack_end_magic(&init_task);//init_task即手工创建的PCB
        smp_setup_processor_id();   //获取当前CPU的硬件ID
        debug_objects_early_init();    //初始化哈希桶
    
        /*
         * Set up the the initial canary ASAP:
         */
        boot_init_stack_canary();  //防止栈溢出
    
        cgroup_init_early();
    
    

    void lockdep_init(void) 函数,lockdep是一个内核调试模块,用来检查内核互斥机制(尤其是自旋锁)潜在的死锁问题。接下来是看到init_task,其在文件linux-3.18.6/init/init_task.c中定义如下:
    struct task_struct init_task = INIT_TASK(init_task);
    可见它其实就是一个task_struct,与用户进程的task_struct一样。相当于《Linux内核分析(二)》中的PCB结构体。
    init_task中保存了一个进程的所有基本信息,如进程状态,栈起始地址,进程号pid等,其特殊之处在于它的pid=0,也就是通常所说的0号进程,0号进程就是我们这样通过手工创建出来的。也就是start_kernel()创建了0号进程。
    0号进程的任务范围是从最早的汇编代码一直到start_kernel()的执行结束。

    2.1号进程的创建

    static noinline void __init_refok rest_init(void)
    {
        int pid;
        rcu_scheduler_starting();
        //很重要,创建一个内核线程,PID=1,创建好了,但不能去调度它
        kernel_thread(kernel_init, NULL, CLONE_FS);
        numa_default_policy();
        ...
    }
    

    在rest_init()函数中有这样一句话:
    kernel_thread(kernel_init, NULL, CLONE_FS);
    其中kernel_thread()的源码在文件linux-3.18.6/kernel/fork.c中定义,如下:

    pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
    {
        return do_fork(flags|CLONE_VM|CLONE_UNTRACED, (unsigned long)fn,
            (unsigned long)arg, NULL, NULL);
    }
    

    这里相当于fork出了新进程来执行kernel_init()函数。

    3.0号进程的转变

    static noinline void __init_refok rest_init(void)
    {
        int pid;
        rcu_scheduler_starting();
        //很重要,创建一个内核线程,PID=1,创建好了,但不能去调度它
        kernel_thread(kernel_init, NULL, CLONE_FS);
        numa_default_policy();
        //很重要,创建第二个内核线程,PID=2,负责管理和调度其它内核线程。
        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);
        init_idle_bootup_task(current);
        schedule_preempt_disabled();
        cpu_startup_entry(CPUHP_ONLINE);
    }
    

    rest_init()在创建了1号、2号进程之后,系统可以正式对外工作了。
    cpu_startup_entry(CPUHP_ONLINE)实际是一个while无限循环,也就是说,0号进程在fork了1号进程并且做了其余的启动工作之后,最后“进化”成为了idle进程。完成其使命,并一直处于内核态中无线循环。

    三.遇到的问题

    • qemu无法正常使用:

      • 下载qemu-system-i836代替可用
    • make menuconfig出错:

      • 下载libncurses5-dev解决
  • 相关阅读:
    初识 Mysql
    Python之协程
    crm 动态一级二级菜单
    admin 后台操作表格
    crm 权限设计
    crm 公户变私户的问题 班级管理 课程管理 学习记录初始化
    crm 添加用户 编辑用户 公户和私户的展示,公户和私户的转化
    crm 数据展示 和分页思想(一)
    python django(forms组件)
    python Django 中间件介绍
  • 原文地址:https://www.cnblogs.com/ppswaggy/p/13904594.html
Copyright © 2011-2022 走看看