基于mykernel 2.0编写一个操作系统内核
Posted 春喜
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于mykernel 2.0编写一个操作系统内核相关的知识,希望对你有一定的参考价值。
一、实验要求
1、按照https://github.com/mengning/mykernel 的说明配置mykernel 2.0,熟悉Linux内核的编译;
2、基于mykernel 2.0编写一个操作系统内核,参照https://github.com/mengning/mykernel 提供的范例代码
3、简要分析操作系统内核核心功能及运行工作机制
二、实验环境
ubuntu-16.04.6
三、实验步骤
1、按照https://github.com/mengning/mykernel 的说明配置mykernel 2.0
下载并编译mykernel,在终端输入以下命令即可
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 # Default configuration is based on \'x86_64_defconfig\' make -j$(nproc) # 编译的时间比较久 sudo apt install qemu # install QEMU
接下来启动mykernel
qemu-system-x86_64 -kernel arch/x86/boot/bzImage
从qemu窗口中您可以看到my_start_kernel在执行,同时my_timer_handler时钟中断处理程序周期性执行。
2、基于mykernel编写一个操作系统
进入mykernel目录可以看到qemu窗口输出的内容的代码mymain.c和myinterrupt.c。当前有一个CPU执行C代码的上下文环境,同时具有中断处理程序的上下文环境,我们通过Linux内核代码模拟了一个具有时钟中断和C代码执行环境的硬件平台。只要在mymain.c基础上继续写进程描述PCB和进程链表管理等代码,在myinterrupt.c的基础上完成进程切换代码,一个可运行的小OS kernel就完成了。
在下载源码后,将mypcb.h,myinterrupt.c和mymain.c这三个文件拷贝到mykernel目录下,即要覆盖之前的mykernel文件夹下mymain.c和myinterrupt.c,并新增头文件mypcb.h
1)、mypcb.h:进程控制块结构体定义
1 #define MAX_TASK_NUM 4 2 #define KERNEL_STACK_SIZE 1024*2 # unsigned long 3 /* CPU-specific state of this task */ 4 struct Thread { 5 unsigned long ip; 6 unsigned long sp; 7 }; 8 9 typedef struct PCB{ 10 int pid; 11 volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */ 12 unsigned long stack[KERNEL_STACK_SIZE]; 13 /* CPU-specific state of this task */ 14 struct Thread thread; 15 unsigned long task_entry; 16 struct PCB *next; 17 }tPCB; 18 19 void my_schedule(void);
该头文件中一些变量和函数的含义:
pid:进程号
state:进程状态,在模拟系统中,所有进程控制块信息都会被创建出来,其初始化值就是-1,如 果被调度运行起来,其值就会变成0
stack:进程使用的堆栈
thread:当前正在执行的线程信息
task_entry:进程入口函数
next:指向下一个PCB,模拟系统中所有的PCB是以链表的形式组织起来的。
函数的声明 my_schedule,它的实现在my_interrupt.c中,在mymain.c中的各个进程函数会根据 一 个全局变量的状态来决定是否调用它,从而实现主动调度。
2)、mymain.c:初始化各个进程并启动0号进程;
1 void __init my_start_kernel(void) 2 { 3 int pid = 0; 4 int i; 5 /* Initialize process 0*/ 6 task[pid].pid = pid; 7 task[pid].state = 0;/* -1 unrunnable, 0 runnable, >0 stopped */ 8 task[pid].task_entry = task[pid].thread.ip = (unsigned long)my_process; 9 task[pid].thread.sp = (unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-1]; 10 task[pid].next = &task[pid]; 11 /*fork more process */ 12 for(i=1;i<MAX_TASK_NUM;i++) 13 { 14 memcpy(&task[i],&task[0],sizeof(tPCB)); 15 task[i].pid = i; 16 //*(&task[i].stack[KERNEL_STACK_SIZE-1] - 1) = (unsigned long)&task[i].stack[KERNEL_STACK_SIZE-1]; 17 task[i].thread.sp = (unsigned long)(&task[i].stack[KERNEL_STACK_SIZE-1]); 18 task[i].next = task[i-1].next; 19 task[i-1].next = &task[i]; 20 } 21 /* start process 0 by task[0] */ 22 pid = 0; 23 my_current_task = &task[pid]; 24 asm volatile( 25 "movl %1,%%esp\\n\\t" /* set task[pid].thread.sp to esp */ 26 "pushl %1\\n\\t" /* push ebp */ 27 "pushl %0\\n\\t" /* push task[pid].thread.ip */ 28 "ret\\n\\t" /* pop task[pid].thread.ip to eip */ 29 : 30 : "c" (task[pid].thread.ip),"d" (task[pid].thread.sp) /* input c or d mean %ecx/%edx*/ 31 ); 32 } 33 34 int i = 0; 35 36 void my_process(void) 37 { 38 while(1) 39 { 40 i++; 41 if(i%10000000 == 0) 42 { 43 printk(KERN_NOTICE "this is process %d -\\n",my_current_task->pid); 44 if(my_need_sched == 1) 45 { 46 my_need_sched = 0; 47 my_schedule(); 48 } 49 printk(KERN_NOTICE "this is process %d +\\n",my_current_task->pid); 50 } 51 } 52 }
3)、myinterrupt.c:时钟中断处理和进程调度算法
1 void my_timer_handler(void) 2 { 3 if(time_count%1000 == 0 && my_need_sched != 1) 4 { 5 printk(KERN_NOTICE ">>>my_timer_handler here<<<\\n"); 6 my_need_sched = 1; 7 } 8 time_count ++ ; 9 return; 10 } 11 12 void my_schedule(void) 13 { 14 tPCB * next; 15 tPCB * prev; 16 17 if(my_current_task == NULL 18 || my_current_task->next == NULL) 19 { 20 return; 21 } 22 printk(KERN_NOTICE ">>>my_schedule<<<\\n"); 23 /* schedule */ 24 next = my_current_task->next; 25 prev = my_current_task; 26 if(next->state == 0)/* -1 unrunnable, 0 runnable, >0 stopped */ 27 { 28 my_current_task = next; 29 printk(KERN_NOTICE ">>>switch %d to %d<<<\\n",prev->pid,next->pid); 30 /* switch to next process */ 31 asm volatile( 32 "pushq %%rbp\\n\\t" /* save rbp of prev */ 33 "movq %%rsp,%0\\n\\t" /* save rsp of prev */ 34 "movq %2,%%rsp\\n\\t" /* restore rsp of next */ 35 "movq $1f,%1\\n\\t" /* save rip of prev */ 36 "pushq %3\\n\\t" 37 "ret\\n\\t" /* restore rip of next */ 38 "1:\\t" /* next process start here */ 39 "popq %%rbp\\n\\t" 40 : "=m" (prev->thread.sp),"=m" (prev->thread.ip) 41 : "m" (next->thread.sp),"m" (next->thread.ip) 42 ); 43 } 44 return; 45 }
3、再次编译运行:
make allnoconfig
make
qemu -kernel arch/x86/boot/bzImage
四、实验总结
通过手动实现 mykernel 操作系统的内核,我学习到了进程的创建、加载以及不同进程之间切换的相关知识。
以上是关于基于mykernel 2.0编写一个操作系统内核的主要内容,如果未能解决你的问题,请参考以下文章