基于 mykernel 实现具有进程切换的基本内核

由SA***411创作

实验环境准备

目标实验环境

  • 实验平台: Windows Hyper-V 虚拟机平台
  • 实验 OS: Ubuntu 18.04
  • Linux 内核版本: Linux 3.9.4
  • mykernel 内核运行平台: qemu-system-i386

搭建实验环境

  • 准备虚拟机并安装 Ubuntu 操作系统

在这里插入图片描述

  • Ubuntu 版本

在这里插入图片描述

  • 安装实验所需工具软件
sudo apt install gcc-4.8 make qemu
cd /usr/bin
sudo ln -s gcc-4.8 gcc

Ubuntu 18.04 默认的gcc编译器的版本是 gcc-7, gcc-5 之后的版本由于调整了一小部分头文件的命名方式,无法直接编译 Linux 3.9.4 内核,因此我使用 gcc-4.8 进行编译。

在这里插入图片描述

运行基础实验

  • 下载 Linux-3.9.4.tar.xz

这里我们使用中科大的镜像站加速下载 http://mirrors.ustc.edu.cn/kernel.org/linux/kernel/v3.x/linux-3.9.4.tar.gz

  • 下载孟宁老师提供的 mykernel 的补丁包

地址 https://raw.github.com/mengning/mykernel/master/mykernel_for_linux3.9.4sc.patch

  • 执行以下命令进行下载和编译运行
mkdir mykernel
cd mykernel
wget http://mirrors.ustc.edu.cn/kernel.org/linux/kernel/v3.x/linux-3.9.4.tar.gz
wget https://raw.github.com/mengning/mykernel/master/mykernel_for_linux3.9.4sc.patch
tar xzvf linux-3.9.4.tar.gz
cd linux-3.9.4
patch -p1 < ../mykernel_for_linux3.9.4sc.patch
make allnoconfig
make
qemu-system-i386 -kernel arch/x86/boot/bzImage

实验过程截图如下

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

实现进程切换

我参考了孟宁老师提供的代码,实现了简单的基于时间片轮转的进程切换,效果如下图所示。

在这里插入图片描述

进程切换代码分析

mypcb.h 代码文件分析

// 最大进程数量
#define MAX_TASK_NUM        4
// 内核栈容量
#define KERNEL_STACK_SIZE   1024*2 # unsigned long
/* CPU-specific state of this task */
// 进程的关键指针
struct Thread {
    unsigned long		ip;
    unsigned long		sp;
};
// PCB结构体
typedef struct PCB{
    int pid;
    volatile long state;	/* -1 unrunnable, 0 runnable, >0 stopped */
    unsigned long stack[KERNEL_STACK_SIZE];
    /* CPU-specific state of this task */
    struct Thread thread;
    unsigned long	task_entry;
    // 指向下一个进程的 PCB
    struct PCB *next;
}tPCB;
//调度函数
void my_schedule(void);

mymain.c 代码文件分析

/*

*/

// PCB 数组,集中存储所有进程的 PCB
tPCB task[MAX_TASK_NUM];
// PCB 指针,指向当前运行的进程
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;
    // 初始化pid = 0 的第一个进程
    /* 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 进程PCB数组所有其他进程
    /*fork more process */
    for(i=1;i<MAX_TASK_NUM;i++)
    {
        // 内存拷贝
        memcpy(&task[i],&task[0],sizeof(tPCB));
        // 修改进程 pid 和 next 指针
        task[i].pid = i;
	//*(&task[i].stack[KERNEL_STACK_SIZE-1] - 1) = (unsigned long)&task[i].stack[KERNEL_STACK_SIZE-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(
    	"movl %1,%%esp\n\t" 	/* set task[pid].thread.sp to esp */
    	"pushl %1\n\t" 	        /* push ebp */
    	"pushl %0\n\t" 	        /* push task[pid].thread.ip */
    	"ret\n\t" 	            /* pop task[pid].thread.ip to eip */
    	: 
    	: "c" (task[pid].thread.ip),"d" (task[pid].thread.sp)	/* input c or d mean %ecx/%edx*/
	);
} 

int i = 0;

// 调度函数
void my_process(void)
{    
    while(1)
    {
        i++;
        if(i%10000000 == 0)
        {
            printk(KERN_NOTICE "this is process %d -\n",my_current_task->pid);
            // 若调度标志为1,则进行调度
            if(my_need_sched == 1)
            {
                my_need_sched = 0;
        	    my_schedule();
        	}
        	printk(KERN_NOTICE "this is process %d +\n",my_current_task->pid);
        }     
    }
}

myinterrupt.c 代码文件分析

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 1
    if(time_count%1000 == 0 && my_need_sched != 1)
    {
        printk(KERN_NOTICE ">>>my_timer_handler here<<<\n");
        my_need_sched = 1;
    } 
    time_count ++ ;  
#endif
    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<<<\n");
    /* 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<<<\n",prev->pid,next->pid);  
    	/* switch to next process */
    	asm volatile(	
        	"pushl %%ebp\n\t" 	    /* save ebp */
        	"movl %%esp,%0\n\t" 	/* save esp */
        	"movl %2,%%esp\n\t"     /* restore  esp */
        	"movl $1f,%1\n\t"       /* save eip */	
        	"pushl %3\n\t" 
        	"ret\n\t" 	            /* restore  eip */
        	"1:\t"                  /* next process start here */
        	"popl %%ebp\n\t"
        	: "=m" (prev->thread.sp),"=m" (prev->thread.ip)
        	: "m" (next->thread.sp),"m" (next->thread.ip)
    	); 
    }  
    return;	
}

操作系统工作逻辑总结

  • 硬件初始化
  • 载入内核代码
  • 启动第一个进程
  • 从第一个进程 fork 其他进程
  • 通过中断处理进行进程切换

致谢

以上内容参考了孟宁老师的mykernel 项目,在此感谢。若转载本文请注明 “参考 https://github.com/mengning/linuxkernel/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值