• 基于mykernel2.0编写一个操作系统内核


    基于mykernel2.0编写一个操作系统内核

    一. 实验准备

    1. 详细要求

    基于mykernel 2.0编写一个操作系统内核

    1. 按照https://github.com/mengning/mykernel 的说明配置mykernel 2.0,熟悉Linux内核的编译;
    2. 基于mykernel 2.0编写一个操作系统内核,参照https://github.com/mengning/mykernel 提供的范例代码
    3. 简要分析操作系统内核核心功能及运行工作机制
    1. 实验环境

    发行版本:Ubuntu 18.04.4 LTS

    处理器:Intel® Core™ i7-8850H CPU @ 2.60GHz × 3

    图形卡:Parallels using AMD® Radeon pro 560x opengl engine

    GNOME:3.28.2

    二. 实验过程

    1. Set up mykernel 2.0

    依次执行如下的指令

    wget https://raw.github.com/mengning/mykernel/master/mykernel-2.0_for_linux-5.4.34.patch
    sudo apt install axel
    axel -n 20 https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/linux-5.4.34.tar.xz
    xz -d linux-5.4.34.tar.xz
    tar -xvf linux-5.4.34.tar
    cd linux-5.4.34
    patch -p1 < ../mykernel-2.0_for_linux-5.4.34.patch
    sudo apt install build-essential libncurses-dev bison flex libssl-dev libelf-dev
    make defconfig 
    make -j$(nproc) 
    sudo apt install qemu 
    qemu-system-x86_64 -kernel arch/x86/boot/bzImage
    

    执行成功并运行后可以看到如下的界面

    1. qemu窗口中输出的内容来自代码mymain.c和myinterrupt.c。先查看一下代码内容

    mymain.c

    #ifdef CONFIG_X86_LOCAL_APIC
    #include <asm/smp.h>
    #endif
    
    
    void __init my_start_kernel(void)
    {
        int i = 0;
        while(1)
        {
            i++;
            if(i%100000 == 0)
                pr_notice("my_start_kernel here  %d 
    ",i);
                
        }
    }
    

    nyinterrupt.c

    /*
     * Called by timer interrupt.
     */
    void my_timer_handler(void)
    {
    	pr_notice("
    >>>>>>>>>>>>>>>>>my_timer_handler here<<<<<<<<<<<<<<<<<<
    
    ");
    }
    
    1. 首先在mykernel目录下增加一个mypcb.h 头文件,用来定义进程控制块(Process Control Block),也就是进程结构体的定义,在Linux内核中是struct tast_struct结构体
    /*
     *  linux/mykernel/mypcb.h
     */
    
    
    #define MAX_TASK_NUM        4
    #define KERNEL_STACK_SIZE   1024*8
    
    
    /* CPU-specific state of this task */
    struct Thread {
        unsigned long       ip;
        unsigned long       sp;
    };
    
    
    typedef struct PCB{
        int pid;
        volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
        char stack[KERNEL_STACK_SIZE];
        /* CPU-specific state of this task */
        struct Thread thread;
        unsigned long   task_entry;
        struct PCB *next;
    }tPCB;
    
    
    void my_schedule(void);
    
    1. 修改mymain.c中的my_start_kernel函数,并在mymain.c中实现了my_process函数,用来作为进程的代码模拟一个个进程,时间片轮转调度。
    #include "mypcb.h"
    
    
    tPCB task[MAX_TASK_NUM];
    tPCB * my_current_task = NULL;
    volatile int my_need_sched = 0;
    
    
    void my_process(void);
    
    
    void __init my_start_kernel(void)
    {
        int pid = 0;
        int i;
        /* Initialize process 0*/
        task[pid].pid = pid;
        task[pid].state = 0;/* -1 unrunnable, 0 runnable, >0 stopped */
        task[pid].task_entry = task[pid].thread.ip = (unsigned long)my_process;
        task[pid].thread.sp = (unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-1];
        task[pid].next = &task[pid];
        /*fork more process */
        for(i=1;i<MAX_TASK_NUM;i++)
        {
            memcpy(&task[i],&task[0],sizeof(tPCB));
            task[i].pid = i;
            task[i].state = -1;
            task[i].thread.sp = (unsigned long)&task[i].stack[KERNEL_STACK_SIZE-1];
            task[i].next = task[i-1].next;
            task[i-1].next = &task[i];
        }
        /* start process 0 by task[0] */
        pid = 0;
        my_current_task = &task[pid];
        asm volatile(
            "movq %1,%%rsp
    	"  /* set task[pid].thread.sp to rsp */
            "pushq %1
    	"          /* push rbp */
            "pushq %0
    	"          /* push task[pid].thread.ip */
            "ret
    	"              /* pop task[pid].thread.ip to rip */
            :
            : "c" (task[pid].thread.ip),"d" (task[pid].thread.sp)   /* input c or d mean %ecx/%edx*/
        );
    }
    
    void my_process(void)
    {
        int i = 0;
        while(1)
        {
            i++;
            if(i%10000000 == 0)
            {
                printk(KERN_NOTICE "this is process %d -
    ",my_current_task->pid);
                if(my_need_sched == 1)
                {
                    my_need_sched = 0;
                    my_schedule();
                }
                printk(KERN_NOTICE "this is process %d +
    ",my_current_task->pid);
            }
        }
    } 
    
    1. 对myinterrupt.c进行修改,my_timer_handler用来记录时间片,时间片消耗完之后完成调度。
    #include "mypcb.h"
    
    
    extern tPCB task[MAX_TASK_NUM];
    extern tPCB * my_current_task;
    extern volatile int my_need_sched;
    volatile int time_count = 0;
    
    
    /*
     * Called by timer interrupt.
     */
    void my_timer_handler(void)
    {
        if(time_count%1000 == 0 && my_need_sched != 1)
        {
            printk(KERN_NOTICE ">>>my_timer_handler here<<<
    ");
            my_need_sched = 1;
        }
        time_count ++ ;
        return;
    }
    
    
    void my_schedule(void)
    {
        tPCB * next;
        tPCB * prev;
    
    
        if(my_current_task == NULL
            || my_current_task->next == NULL)
        {
          return;
        }
        printk(KERN_NOTICE ">>>my_schedule<<<
    ");
        /* schedule */
        next = my_current_task->next;
        prev = my_current_task;
        if(next->state == 0)/* -1 unrunnable, 0 runnable, >0 stopped */
        {
          my_current_task = next;
          printk(KERN_NOTICE ">>>switch %d to %d<<<
    ",prev->pid,next->pid);
          /* switch to next process */
          asm volatile(
             "pushq %%rbp
    	"       /* save rbp of prev */
             "movq %%rsp,%0
    	"     /* save rsp of prev */
             "movq %2,%%rsp
    	"     /* restore  rsp of next */
             "movq $1f,%1
    	"       /* save rip of prev */
             "pushq %3
    	"
             "ret
    	"               /* restore  rip of next */
             "1:	"                  /* next process start here */
             "popq %%rbp
    	"
            : "=m" (prev->thread.sp),"=m" (prev->thread.ip)
            : "m" (next->thread.sp),"m" (next->thread.ip)
          );
        }
        return;
    }
    
    1. 更改后的运行结果:

    进程切换过程中进程0和进程1的堆栈和相关寄存器的变化过程大致如下:

    • pushq %%rbp 保存prev进程(本例中指进程0)当前RBP寄存器的值到prev进程的堆栈;
    • movq %%rsp,%0 保存prev进程(本例中指进程0)当前RSP寄存器的值到prev->thread.sp,这时RSP寄存器指向进程的栈顶地址,实际上就是将prev进程的栈顶地址保存;%0、%1...指这段汇编代码下面输入输出部分的编号。
    • movq %2,%%rsp 将next进程的栈顶地址next->thread.sp放入RSP寄存器,完成了进程0和进程1的堆栈切换。
    • movq $1f,%1 保存prev进程当前RIP寄存器值到prev->thread.ip,这里$1f是指标号1。
    • pushq %3 把即将执行的next进程的指令地址next->thread.ip入栈。
    • ret 就是将压入栈中的next->thread.ip放入rip寄存器,rip寄存器现在存储next进程的指令。
    • 1: 标号1是一个特殊的地址位置,该位置的地址是$1f。
    • popq %%rbp 将next进程堆栈基地址从堆栈中恢复到RBP寄存器中。

    三. 总结

    这次实验主要做了如下的事情:

    • 学习并完成实验环境的配置的搭建
    • 学习并了解Linux内核编译相关知识
    • 通过实践加深对编译的学习与体会
    • 基于mykernel 2.0编写一个操作系统内核
    • 思考代码执行的各项原理
  • 相关阅读:
    document.getElementById("mytxt").style.left=""style.left在IE的FF中注意
    asp.net 用户控件中 使用相对路径的解决方法 图片路径问题(用户控件、图片路径) ,ResolveUrl
    探索 Block (一) (手把手讲解Block 底层实现原理)
    iOS 多线程开发 (概念与API简介)
    iOS 性能小点
    iOS runtime (二)(runtime学习之AutoCoding源码分析)
    探索 NSRunLoop (二)(NSRunLoop 自己动手实现SimpleRunLoop)
    iOS NSNotificationCenter (自己实现一个通知中心XMCNotificationCenter)
    iOS runtime (三)(runtime学习之YYModel源码分析)
    iOS runtime(一)(runtime 分析理解)
  • 原文地址:https://www.cnblogs.com/femery/p/12869490.html
Copyright © 2020-2023  润新知