zoukankan      html  css  js  c++  java
  • 基于mykernel 2.0编写一个操作系统内核

    资源下载:https://github.com/mengning/mykernel

    实验内容:

    1、配置实验环境,完成Linux内核编译。

    2、对系统源码进行修改,基于mykernel 2.0实现一个简单的操作系统内核。

    3、简要分析操作系统内核核心功能及运行工作机制。

    实验环境:

    VMWare虚拟机下的Ubuntu18.04.4,实验采用的内核版本为linux-5.4.34。


    1 内核编译

    1.1 准备工作

    1.1.1 修改镜像源地址

    为了节省资源下载时间,使用国内镜像源。

    cd /etc/apt/
    sudo cp sources.list sources.list.bk
    sudo gedit sources.list
    
    deb http://mirrors.aliyun.com/ubuntu bionic main multiverse restricted universe
    deb http://mirrors.aliyun.com/ubuntu bionic-updates main multiverse restricted universe
    deb http://mirrors.aliyun.com/ubuntu bionic-security main multiverse restricted universe
    deb http://mirrors.aliyun.com/ubuntu bionic-proposed main multiverse restricted universe
    deb http://mirrors.aliyun.com/ubuntu bionic-backports main multiverse restricted universe
    

    将sources.list修改保存后,更新apt源。

    sudo apt-get update
    

    1.1.2 添加hosts映射

    新增GitHub资源域名与对应IP的映射。

    sudo vi /etc/hosts
    
    151.101.76.133 raw.githubusercontent.com
    

    1.1.3 安装axel

    多线程下载工具,用于下载Linux内核。

    sudo apt install axel
    

    1.2 下载内核补丁

    wget https://raw.github.com/mengning/mykernel/master/mykernel-2.0_for_linux-5.4.34.patch
    

    补丁文件如下,根据diff对比,主要做了以下改动:

    • 在时钟中断时调用自定义的my_timer_handler函数,打印输出>>>>>>>my_timer_handler here<<<<<<<<
    • 在start_kernel.h与timer.h中分别声明my_start_kernel与my_timer_handler函数
    • 在main.c中调用my_start_kernel函数
    • 在Makefile中新增mykernel的编译路径
    • 创建mykernel的Makefile文件
    • 编写myinterrupt.c与mymain.c文件,mymain.c中模拟系统运行,每100000次循环输出一次
    diff -Naur linux-5.4.34/arch/x86/kernel/time.c linux-5.4.34-mykernel/arch/x86/kernel/time.c
    --- linux-5.4.34/arch/x86/kernel/time.c	2020-04-21 15:05:05.000000000 +0800
    +++ linux-5.4.34-mykernel/arch/x86/kernel/time.c	2020-04-25 21:58:16.436717811 +0800
    @@ -16,6 +16,7 @@
     #include <linux/irq.h>
     #include <linux/i8253.h>
     #include <linux/time.h>
    +#include <linux/timer.h>
     #include <linux/export.h>
     
     #include <asm/vsyscall.h>
    @@ -59,6 +60,7 @@
     static irqreturn_t timer_interrupt(int irq, void *dev_id)
     {
     	global_clock_event->event_handler(global_clock_event);
    +    	my_timer_handler();
     	return IRQ_HANDLED;
     }
     
    diff -Naur linux-5.4.34/include/linux/start_kernel.h linux-5.4.34-mykernel/include/linux/start_kernel.h
    --- linux-5.4.34/include/linux/start_kernel.h	2020-04-21 15:05:05.000000000 +0800
    +++ linux-5.4.34-mykernel/include/linux/start_kernel.h	2020-04-25 22:00:17.304717811 +0800
    @@ -9,6 +9,7 @@
        up something else. */
     
     extern asmlinkage void __init start_kernel(void);
    +extern void __init my_start_kernel(void);
     extern void __init arch_call_rest_init(void);
     extern void __ref rest_init(void);
     
    diff -Naur linux-5.4.34/include/linux/timer.h linux-5.4.34-mykernel/include/linux/timer.h
    --- linux-5.4.34/include/linux/timer.h	2020-04-21 15:05:05.000000000 +0800
    +++ linux-5.4.34-mykernel/include/linux/timer.h	2020-04-25 21:56:45.064717811 +0800
    @@ -193,6 +193,8 @@
     
     extern void init_timers(void);
     extern void run_local_timers(void);
    +extern void my_timer_handler(void);
    +
     struct hrtimer;
     extern enum hrtimer_restart it_real_fn(struct hrtimer *);
     
    diff -Naur linux-5.4.34/init/main.c linux-5.4.34-mykernel/init/main.c
    --- linux-5.4.34/init/main.c	2020-04-21 15:05:05.000000000 +0800
    +++ linux-5.4.34-mykernel/init/main.c	2020-04-25 22:01:13.476717811 +0800
    @@ -781,6 +781,7 @@
     	arch_post_acpi_subsys_init();
     	sfi_init_late();
     
    +    	my_start_kernel();
     	/* Do the rest non-__init'ed, we're now alive */
     	arch_call_rest_init();
     }
    diff -Naur linux-5.4.34/Makefile linux-5.4.34-mykernel/Makefile
    --- linux-5.4.34/Makefile	2020-04-21 15:05:05.000000000 +0800
    +++ linux-5.4.34-mykernel/Makefile	2020-04-25 22:02:47.144717811 +0800
    @@ -1012,7 +1012,7 @@
     export MODORDER := $(extmod-prefix)modules.order
     
     ifeq ($(KBUILD_EXTMOD),)
    -core-y		+= kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/
    +core-y		+= kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/ mykernel/
     
     vmlinux-dirs	:= $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) 
     		     $(core-y) $(core-m) $(drivers-y) $(drivers-m) 
    diff -Naur linux-5.4.34/mykernel/Makefile linux-5.4.34-mykernel/mykernel/Makefile
    --- linux-5.4.34/mykernel/Makefile	1970-01-01 08:00:00.000000000 +0800
    +++ linux-5.4.34-mykernel/mykernel/Makefile	2020-04-25 17:14:13.537908421 +0800
    @@ -0,0 +1,6 @@
    +#
    +# Makefile for the linux mykernel.
    +#
    +
    +obj-y     = mymain.o myinterrupt.o
    +
    diff -Naur linux-5.4.34/mykernel/myinterrupt.c linux-5.4.34-mykernel/mykernel/myinterrupt.c
    --- linux-5.4.34/mykernel/myinterrupt.c	1970-01-01 08:00:00.000000000 +0800
    +++ linux-5.4.34-mykernel/mykernel/myinterrupt.c	2020-04-25 19:09:50.612555999 +0800
    @@ -0,0 +1,44 @@
    +/*
    + *  linux/mykernel/myinterrupt.c
    + *
    + *  Kernel internal my_timer_handler
    + *
    + *  Copyright (C) 2013  Mengning
    + *
    + */
    +#include <linux/kernel_stat.h>
    +#include <linux/export.h>
    +#include <linux/interrupt.h>
    +#include <linux/percpu.h>
    +#include <linux/init.h>
    +#include <linux/mm.h>
    +#include <linux/swap.h>
    +#include <linux/pid_namespace.h>
    +#include <linux/notifier.h>
    +#include <linux/thread_info.h>
    +#include <linux/time.h>
    +#include <linux/jiffies.h>
    +#include <linux/posix-timers.h>
    +#include <linux/cpu.h>
    +#include <linux/syscalls.h>
    +#include <linux/delay.h>
    +#include <linux/tick.h>
    +#include <linux/kallsyms.h>
    +#include <linux/irq_work.h>
    +#include <linux/sched.h>
    +#include <linux/sched/sysctl.h>
    +#include <linux/slab.h>
    +
    +#include <asm/uaccess.h>
    +#include <asm/unistd.h>
    +#include <asm/div64.h>
    +#include <asm/timex.h>
    +#include <asm/io.h>
    +
    +/*
    + * Called by timer interrupt.
    + */
    +void my_timer_handler(void)
    +{
    +	pr_notice("
    >>>>>>>>>>>>>>>>>my_timer_handler here<<<<<<<<<<<<<<<<<<
    
    ");
    +}
    diff -Naur linux-5.4.34/mykernel/mymain.c linux-5.4.34-mykernel/mykernel/mymain.c
    --- linux-5.4.34/mykernel/mymain.c	1970-01-01 08:00:00.000000000 +0800
    +++ linux-5.4.34-mykernel/mykernel/mymain.c	2020-04-25 19:10:27.635058000 +0800
    @@ -0,0 +1,91 @@
    +/*
    + *  linux/mykernel/mymain.c
    + *
    + *  Kernel internal my_start_kernel
    + *
    + *  Copyright (C) 2013  Mengning
    + *
    + */
    +#include <linux/types.h>
    +#include <linux/module.h>
    +#include <linux/proc_fs.h>
    +#include <linux/kernel.h>
    +#include <linux/syscalls.h>
    +#include <linux/stackprotector.h>
    +#include <linux/string.h>
    +#include <linux/ctype.h>
    +#include <linux/delay.h>
    +#include <linux/ioport.h>
    +#include <linux/init.h>
    +#include <linux/initrd.h>
    +#include <linux/acpi.h>
    +#include <linux/tty.h>
    +#include <linux/percpu.h>
    +#include <linux/kmod.h>
    +#include <linux/vmalloc.h>
    +#include <linux/kernel_stat.h>
    +#include <linux/start_kernel.h>
    +#include <linux/security.h>
    +#include <linux/smp.h>
    +#include <linux/profile.h>
    +#include <linux/rcupdate.h>
    +#include <linux/moduleparam.h>
    +#include <linux/kallsyms.h>
    +#include <linux/writeback.h>
    +#include <linux/cpu.h>
    +#include <linux/cpuset.h>
    +#include <linux/cgroup.h>
    +#include <linux/efi.h>
    +#include <linux/tick.h>
    +#include <linux/interrupt.h>
    +#include <linux/taskstats_kern.h>
    +#include <linux/delayacct.h>
    +#include <linux/unistd.h>
    +#include <linux/rmap.h>
    +#include <linux/mempolicy.h>
    +#include <linux/key.h>
    +#include <linux/buffer_head.h>
    +#include <linux/debug_locks.h>
    +#include <linux/debugobjects.h>
    +#include <linux/lockdep.h>
    +#include <linux/kmemleak.h>
    +#include <linux/pid_namespace.h>
    +#include <linux/device.h>
    +#include <linux/kthread.h>
    +#include <linux/sched.h>
    +#include <linux/signal.h>
    +#include <linux/idr.h>
    +#include <linux/kgdb.h>
    +#include <linux/ftrace.h>
    +#include <linux/async.h>
    +#include <linux/sfi.h>
    +#include <linux/shmem_fs.h>
    +#include <linux/slab.h>
    +#include <linux/perf_event.h>
    +#include <linux/file.h>
    +#include <linux/ptrace.h>
    +#include <linux/blkdev.h>
    +#include <linux/elevator.h>
    +
    +#include <asm/io.h>
    +#include <asm/bugs.h>
    +#include <asm/setup.h>
    +#include <asm/sections.h>
    +#include <asm/cacheflush.h>
    +
    +#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);
    +            
    +    }
    +}
    diff -Naur linux-5.4.34/mykernel/README.md linux-5.4.34-mykernel/mykernel/README.md
    --- linux-5.4.34/mykernel/README.md	1970-01-01 08:00:00.000000000 +0800
    +++ linux-5.4.34-mykernel/mykernel/README.md	2020-04-25 22:18:46.512717811 +0800
    @@ -0,0 +1,21 @@
    +mykernel 2.0
    +==========
    +Develop your own OS kernel by reusing Linux infrastructure, based on x86-64/Linux Kernel 5.4.34.
    +
    +## Set up mykernel 2.0 in Ubuntu 18.04
    +
    +```
    +sudo apt install build-essential
    +sudo apt install axel
    +sudo apt install qemu # install QEMU
    +sudo apt install libncurses-dev bison flex libssl-dev libelf-dev
    +wget https://raw.github.com/mengning/mykernel/master/mykernel-2.0_for_linux-5.3.34.patch
    +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.3.34.patch
    +make defconfig # Default configuration is based on 'x86_64_defconfig'
    +make -j$(nproc)
    +qemu-system-x86_64 -kernel arch/x86/boot/bzImage
    +```
    

    1.3 下载内核

    sudo 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
    

    1.4 安装补丁

    cd linux-5.4.34
    sudo apt install patch
    patch -p1 < ../mykernel-2.0_for_linux-5.4.34.patch
    

    1.5 编译内核

    在虚拟机环境下,如果物理机支持超线程,可以配置虚拟机为双核四线程。

    编译时间大致四五分钟左右,如果虚拟机默认配置单核单线程,使用defconfig需要较长的编译时间。

    sudo apt install build-essential libncurses-dev bison flex libssl-dev libelf-dev
    make defconfig
    make -j$(nproc)
    

    1.6 安装QEMU模拟器

    此处QEMU用于模拟硬件设备,通过模拟一台独立运行操作系统的虚拟机,运行编译后的系统。

    sudo apt install qemu # install QEMU
    qemu-system-x86_64 -kernel arch/x86/boot/bzImage
    


    至此,Linux内核编译完成,大致的配置流程为:

    • 下载内核补丁
    • 下载系统内核
    • 通过给系统打补丁后编译运行,观察内核运行与中断的输出

    2 修改内核

    首先,在mykernel目录下创建mypcb.h头文件:

    • Thread结构体模拟指令指针与堆栈指针

    • PCB结构体实现进程控制块,主要包含进程句柄,状态,栈,线程信息,进程函数等,next以链表形式链接进程

    #define MAX_TASK_NUM        4
    #define KERNEL_STACK_SIZE   1024*2
    
    struct Thread {
        unsigned long		ip;
        unsigned long		sp;
    };
    
    typedef struct PCB{
        int pid;
        volatile long state;	/* -1 unrunnable, 0 runnable, >0 stopped */
        unsigned long stack[KERNEL_STACK_SIZE];
        struct Thread thread;
        unsigned long	task_entry;
        struct PCB *next;
    }tPCB;
    
    void my_schedule(void);
    

    之后创建myinterrupt.c文件,实现中断效果以及进程间的切换:

    #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.
     * it runs in the name of current running process,
     * so it use kernel stack of current running process
     */
    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)
        {        
        	my_current_task = next; 
        	printk(KERN_NOTICE ">>>switch %d to %d<<<
    ",prev->pid,next->pid);  
        	/* switch to next process */
        	asm volatile(	
            	"pushq %%rbp
    	"
            	"movq %%rsp,%0
    	"
            	"movq %2,%%rsp
    	"
            	"movq $1f,%1
    	"
            	"pushq %3
    	" 
            	"ret
    	"
            	"1:	"
            	"popq %%rbp
    	"
            	: "=m" (prev->thread.sp),"=m" (prev->thread.ip)
            	: "m" (next->thread.sp),"m" (next->thread.ip)
        	); 
        }  
        return;	
    }
    

    最后在mymain.c中,根据0号进程fork出其他进程,形成进程的环状调用:

    #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;
        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].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
    	"
        	"pushq %1
    	"
        	"pushq %0
    	"
        	"ret
    	"
        	: 
        	: "c" (task[pid].thread.ip),"d" (task[pid].thread.sp)
    	);
    } 
    
    int i = 0;
    
    void my_process(void)
    {    
        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);
            }     
        }
    }
    

    以上代码的核心在于两段内嵌汇编代码,mymain.c与myinterrupt.c中分别实现了0号进程的启动与进程间切换:

    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*/
    	);
    

    mymain.c中将0号进程栈顶信息存入rsp寄存器,通过将当前进程rbp与指令指针压栈,再借用ret指令返回0号进程ip指向的my_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)
        	); 
    

    myinterrupt.c中先将当前的rbp压栈,然后保存当前进程的rsp信息,完成后将其更新为下一个进程的rsp。

    之后将即将运行的进程IP入栈,通过ret指令将下一个进程的IP送入rip寄存器。

    最后将切换后的进程堆栈基地址从堆栈中恢复到rbp寄存器中。

    3 核心功能

    Linux操作系统主要有以下核心功能:

    • 进程管理

      负责管理CPU资源,以便让各个进程可以以尽量公平的方式访问CPU。

    • 内存管理

      负责管理Memory(内存)资源,以便让各个进程可以安全地共享机器的内存资源。

    • 文件系统

      Linux内核将不同功能的外部设备,抽象为统一的文件操作接口(open、close、read、write等)。

    • 网络管理

      负责管理系统的网络设备,并实现各类网络标准。

    • 硬件驱动

      将系统操作映射到物理设备,除了处理器,内存等个别实体外,一般设备控制操作都由寻址设备相关的代码进行。

    • 进程间通信

      不管理硬件,只负责Linux系统中进程之间的通信。

  • 相关阅读:
    I NEED A OFFER!
    水题 Codeforces Round #303 (Div. 2) A. Toy Cars
    模拟 HDOJ 5099 Comparison of Android versions
    模拟 HDOJ 5095 Linearization of the kernel functions in SVM
    贪心 HDOJ 5090 Game with Pearls
    Kruskal HDOJ 1863 畅通工程
    Kruskal HDOJ 1233 还是畅通工程
    并查集 HDOJ 1232 畅通工程
    DFS/并查集 Codeforces Round #286 (Div. 2) B
    水题 Codeforces Round #286 (Div. 2) A Mr. Kitayuta's Gift
  • 原文地址:https://www.cnblogs.com/ustca/p/12874785.html
Copyright © 2011-2022 走看看