实验楼 实验二
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、搭建内核
搭建起来的内核启动效果如下:
3、完成一个简单的时间片轮转多道程序内核代码
编辑mypcb.h mymain.c myinterrupt.c三个文件
mymain.c:
myinterrupt.c :
mypcb.h :
再次make,重新编译后运行:
4、关键代码分析
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
5、总结
(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),并能够模拟处理器、设备和内存等关键组件。