实验三:Mykernel实验
1、进入Shell终端
输入如下代码:
$ cd ~/LinuxKernel/linux-3.9.4
$ rm -rf mykernel
$ patch -p1 < ../mykernel_for_linux3.9.4sc.patch
$ make allnoconfig
$ make
$ qemu -kernel arch/x86/boot/bzImage
代码分析:
patch -p1 < ../mykernel_for_linux3.9.4sc.patch: 应用名为mykernel_for_linux3.9.4sc.patch的补丁到当前目录下的文件系统中,该补丁文件会修改源代码文件。
make allnoconfig:使用默认配置生成配置文件.config
,该配置文件将会编译出所有可用的内核模块。
make:运行make命令编译内核,生成可引导的Linux内核镜像
qemu -kernel arch/x86/boot/bzImage:使用qemu虚拟机运行生成的内核镜像文件bzImage
。这将启动虚拟机并加载内核镜像。
2、搭建内核
搭建起来的内核启动效果如下:
内部代码解释:在Linux-3.9.4内核源代码根目录下进入mykernel目录,可以看到QEMU窗口输出的mymain.c和myinterrupt.c的代码。
进入mymain.c:函数my_start_kernel(void)函数,每执行100000次要输出“my_start_kernel here i”。
进入myinterrupt.c:可以看出mymain.c中的代码在不停地被执行,同时有一个中断处理程序的上下文环境,周期性地产生时钟中断信号,能够触发mykernel.c中的my_timer_hander(void)函数。
这样就模拟了一个带有时钟中断的x86CPU。
3、关键代码分析
1.mypcb.h头文件,用来定义进程控制块
2.mymain.c是mykernel内核代码的入口,负责初始化内核的各个组成部分
3.myinterrupt.c中断处理和进程调度
#关键代码
if(next->state == 0)/* -1 unrunnable, 0 runnable, >0 stopped */
{//save current scene
/* switch to next process */
asm volatile(
"pushl %%ebp\n\t" /* 压栈,保存当前ebp到堆栈*/
"movl %%esp,%0\n\t" /* 保存当前ESP到当前PCB中 */
"movl %2,%%esp\n\t" /* 将下一个进程的堆栈栈顶值存到ESP寄存器 */
"movl $1f,%1\n\t" /* s保存当前进程的Eip值,下次恢复进程时取这里的值 */
"pushl %3\n\t" /*压栈 */
"ret\n\t" /* 出栈标号1到eip寄存器 */
"1:\t" /*标号1,也就是next进程开始执行的位置 */
"popl %%ebp\n\t" /*恢复EBP寄存器的值 */
: "=m" (prev->thread.sp),"=m" (prev->thread.ip)
: "m" (next->thread.sp),"m" (next->thread.ip)
);
my_current_task = next;//switch to the next task
printk(KERN_NOTICE " switch from %d process to %d process\n >>>process %d running!!!<<<\n\n",prev->pid,next->pid,next->pid);
}
else /*next该进程第一次被执行*/
{
next->state = 0;
my_current_task = next;
printk(KERN_NOTICE " switch from %d process to %d process\n >>>process %d running!!!<<<\n\n\n",prev->pid,next->pid,next->pid);
/* switch to new process */
asm volatile(
"pushl %%ebp\n\t" /* save ebp */
"movl %%esp,%0\n\t" /* save esp */
"movl %2,%%esp\n\t" /* restore esp */
"movl %2,%%ebp\n\t" /* restore ebp */
"movl $1f,%1\n\t" /* save eip */
"pushl %3\n\t"
"ret\n\t" /* restore eip */
: "=m" (prev->thread.sp),"=m" (prev->thread.ip)
: "m" (next->thread.sp),"m" (next->thread.ip)
);
}
return;
}//end of my_schedule
4、总结
(1)堆栈相关的寄存器和堆栈操作
- ESP(栈顶指针寄存器)
- EBP(基址指针寄存器):基址指针(指向栈顶),在C语言中用作记录当前函数调用的基址。
- EAX:用于暂存一些数值,函数返回值默认使用EAX寄存器存储并返回给上一级调用函数。
- EIP:指示将要执行的下一条指令在存储器中的地址。(总是指向某一条指令的地址)
- CS(代码段寄存器)
- push 压栈,栈顶地址减少四个字节
- pop 出栈,栈顶地址增加四个字节
- call 函数调用,调用一个地址。将当前CS:EIP的值压入栈顶,CS:EIP指向被调用函数的入口地址。
- ret 函数返回,从栈顶弹出原来保存在这里的CS:EIP的值,放入CS:EIP中去。
- enter 用来建立函数堆栈
- leave 用来撤销函数堆栈
(2)Make:Make是一个用于自动化编译的工具,通过读取Makefile文件来确定代码之间的依赖关系,并根据需要编译这些文件。Makefile是一个包含了编译规则的文本文件,它描述了源代码、目标文件和可执行文件之间的关系。
qemu:一个虚拟化工具,它允许在一个宿主机上模拟运行不同体系结构的操作系统.它提供了一个虚拟机监控程序(VMM),并能够模拟处理器、设备和内存等关键组件。