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

早过忘川 提交于 2020-08-07 09:40:31

一、实验要求

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 操作系统的内核,我学习到了进程的创建、加载以及不同进程之间切换的相关知识。

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!