通过分析、编译GeekOS系统的源代码并生成磁盘映射文件,来进一步了解操作系统的原理。并在Bochs模拟器下模拟启动,来了解操作系统的基本原理和系统的启动过程。程序在Linux系统环境下使用。当然你也可以在Windows下安装虚拟机。
1 实验目的
通过分析、编译GeekOS系统的源代码并生成磁盘映射文件,来进一步了解操作系统的原理。并在Bochs模拟器下模拟启动,来了解操作系统的基本原理和系统的启动过程。
2 实验要求
project0:系统配置/启动
目的:了解GeekOS的源代码,配置系统运行环境,并在Bochs模拟器下面运行。
任务:在内核中加入代码,打印出“Hello From Sensory! Mrl CopyRights”。然后根据代码要求,回显键盘输入并统计输入的字符数,直到终止键(Control+d)结束,打印”Ctrl+d Is Entered! Program Ended!”结束。
3 主要开发资源介绍
本实验所需资源:Fedora Core 3、Geekos-0.3.0、Bochs 2.2.6、Gedit、SourceInsight 3.50、 Nasm汇编语言编译器等。
3.1 操作系统
Linux操作系统。Redhat 自9.0以后,不再发布桌面版的,而是把这个项目与开源社区合作,于是就有了Fedora 这个 Linux 发行版。Fedora 可以说是Redhat 桌面版本的延续,只不过是与开源社区合作。
3.2 实验系统
GeekOS 是一个教育操作系统内核,它由Maryland大学David Hovemeyer所开发的基于X86的微内核操作系统。它尽量的简化了系统,但却提供了现代操作系统必须的最基本的功能,例如,虚拟内存,一个文件系统和交互进程通信。本实验使用的是geekos-0.3.0版。
3.3 仿真器介绍
Bochs是一个x86硬件平台的模拟器。换句话说,它可以模拟各种硬件的配置。当启动到Bochs时,看起来就好像你在自己的PC上启动了另外一个PC。Bochs模拟的是整个PC平台,包括I/O设备、内存和BIOS。更为有趣的是,甚至可以不使用PC硬件来运行Bochs。事实上,它可以在任何编译运行Bochs的平台上模拟x86硬件。通过改变配置,可以指定使用的CPU(386、486或者586),以及内存大小等。一句话,Bochs是电脑里的“PC”。根据需要,Bochs还可以模拟多台PC,此外,它甚至还有自己的电源按钮。注释行用#开头.
对Bochs的手动配置主要通过Bochsrc.txt文件来实现。可通过该文件的修改来规定启动方式(软盘、硬盘),用来模拟软盘或硬盘的映像文件等。
4 软件安装步骤
4.1 系统安装
光盘启动Linux安装程序,格式化linux分区和swap分区。安装过程可以选择安装。如果自定义可以安装开发工具包。其中默认安装包含本试验需要的开发工具。除了nasm软件。如果自定义,也可以选中安装。安装完重启即可。
4.2 等软件的安装
开发工具包里,有nasm软件。选中安装即可。如果安装系统没有安装。可以在以后的添加删除程序里安装,不过要安装盘。和windows安装其他软件类似。安装即可。
4.3 下载解压
网上下载Geekos-0.3.0。http://nchc.dl.sourceforge.net/sourceforge/geekos/geekos-0.3.0.zip
在linux下载为Geekos-0.3.0.tar.gz可以通过linux下的解压打开文件。或者通过如下命令解压:
gunzip Geekos-0.3.0.tar.gz
这样你将会看到Geekos-0.3.0.tar文件
tar Geekos-0.3.0.tar
这样你就会看到Geekos-0.3.0目录。
解压成功。
4.4 下载安装
下载解压类似Geekos。我下载的是bin文件。运行./*.bin后需要进入其目录运行安装命令:./configure
后可以运行bochs。在安装时需要用到make install可是我用的时候运行不成功,但bochs可以使用了。如果不是下载的bin文件,可以下载rpm文件,可以直接运行安装。
5 实践Project0
5.1 项目分析
在这个项目里面主要了解两部分的内容:内核线程和键盘处理,相应的文件是kthread.c和keyboard.c。程序里的EchoCount部分,有需要用的函数来完成项目。
1)Start_Kernel_Thread函数
①Start_Kernel_Thread函数(在kthread.c中)主要功能:建立一个内核线程。
内核线程结构的定义如下:
struct Kernel_Thread {
unsigned long esp;
volatile unsigned long numTicks;
int priority;
DEFINE_LINK( Thread_Queue, Kernel_Thread );
void* stackPage;
struct User_Context* userContext;
struct Kernel_Thread* owner;
int refCount;
Boolean alive;
struct Mutex joinLock;
struct Condition joinCond;
};
esp字段用来存放一个线程挂起的堆栈指针;
stackPage字段指向内核线程的堆栈页面
numTicks和priority分别被调度程序用来实现基于先占权和基于优先权的时间片调度。
DEFINE_LINK宏定义一个内核线程在线程队列上时的前一个和后一个字段。
userContext字段如果不为空,则指向一个线程用户环境,它是一个允许线程执行用户模式的代码和数据的组合段。
内核线程有两种方式创建。在内核里独立运行的线程可通过Start_Kernel_Thread()函数来创建,该函数通过一个指针指向一个执行线程体的启动函数。线程所执行的用户模式的程序由Start_User_Thread()函数创建,并且用一指针指向一个用户环境和用户环境内存中代码入口点的地址。调用Exit()函数销毁内核线程。
首先在主程序里调用Start_Kernel_Thread 开始创建一条内核线程:Mythread=Start_Kernel_Thread (&MyFunction,0,PRIORITY_NORMAL,false);
入口参数分别为:函数地址,函数参数(无参数就写0),优先级设定,线程属性(false为内核线程,true为用户线程),返回值Mythread 的数据类型是static struct Kernel_Thread* thread
②Start_Kernel_Thread 完成的工作:
◎ Create_Thread(priority, detached) //根据优先级创建一条线程
kthread = Alloc_Page() //为线程分配内存空间
stackPage = Alloc_Page()
Init_Thread(kthread, stackPage, priority, detached)
Add_To_Back_Of_All_Thread_List(&s_allThreadList, kthread)
◎ Setup_Kernel_Thread(kthread, startFunc, arg) //配置内核线程的初始化
◎ Make_Runnable_Atomic(kthread); //设置线程运行的原子性操作
Disable_Interrupts(); //禁止中断
Make_Runnable(kthread); //线程运行
Enable_Interrupts(); //使能中断
2)编写线程子程序 void EchoCount()
3)Geekos是如何处理键盘代码
在keyboard.c里面提供了一个功用函数Keycode Wait_For_Key(void),循环等待一个键盘事件,然后返回一个16位的数据 Keycode型的, 在keyboard.h里定义了所有的键盘代码。Read_Key(Keycode* keycode)函数可以处理队列键盘按键,可以保存到队列中并输出。关于Keycode的定义是:
低8位用来表示键盘值,通过s_scanTableNoShift和s_scanTableWithShift这两个数组来转换相应的键盘码为所表示字符的ASCII码,高六位分别是:
KEY_SPECIAL_FLAG (特殊键,比如F1,F2) 用返回的值key&0x0100 就可以判断是否按下特殊健,1为有效,说明是特殊健,0则不是,以下的几种情况类似
KEY_KEYPAD_FLAG (小键盘键) 0x0200
KEY_SHIFT_FLAG (左,右SHIFT) 0x1000
KEY_ALT_FLAG (左,右ALT) 0x2000
KEY_CTRL_FLAG (左,右CTRL) 0x4000
KEY_RELEASE_FLAG (键弹起来标志位) 0x8000
5.2 编辑main.c文件
添加如下代码:
void EchoCount()
{
Keycode keycode;
int count;
count=0;
while (1)
{
if ( Read_Key( &keycode ) )
{
if((keycode & 0x4000) == 0x4000)
{
if((Wait_For_Key() & 0x00ff) == 'd') //取末8位,与d的ASCI码相比
//可以用0x64替代’d’,效果相同
{
//Print("%c",Wait_For_Key());
Set_Current_Attr(ATTRIB(BLACK, RED));
Print("Ctrl+d Is Entered! Program Ended!");
Exit(1);
}
}
else if ( !(keycode & KEY_SPECIAL_FLAG) &&
!(keycode & KEY_RELEASE_FLAG) ) //键是否释放
{
keycode &= 0xff; //取keycode的末8位
count=count+1;
Set_Current_Attr(ATTRIB(BLACK, CYAN));
Print( "%c", (keycode == '/r') ? '/n' : keycode );
if(keycode=='/r')
{
count=count-1;//回车减1
Set_Current_Attr(ATTRIB(AMBER, BLUE));
Print("The counnts is %d ",count);
Print("/n");
count=0;
}
}
}
}
}
在main()函数启动线程调用EchoCount():
main()添加
struct Kernel_Thread *kerThd;
kerThd = Start_Kernel_Thread(&EchoCount, 0 , PRIORITY_NORMAL, false);
6 编译运行
进入Build目录下:
$ make depend
$ make
若没有出错在geekos-0.3.0/src/project0/build生成镜像文件fd.img。我的出错:按提示替换depend.mak文件里的geekos/# 1 "/root/Desktop/geekos-0.3.0/src/project0/build//" 全部替换为空并保存。或者删除。修改后重新make。
进入目录/root/Desktop/geekos-0.3.0/src/project0/build
修改.bochsrc
vgaromimage:/root/Desktop/bochs-2.2.6/bios/VGABIOS-lgpl-latest
romimage: file=/root/Desktop/bochs-2.2.6/bios/BIOS-bochs-latest,
address=0xf0000
#newharddrivesupport: enabled=1
保存后,运行bochs按提示运行程序。或者运行bochs –q。
上述具体过程如下图所示:
7 运行结果图
7.1 小结
在Linux下的文件归档在Fedora下无法归档隐藏文件。而系统中需要用到.bochsrc文件的配置。在归档保存到Windows下可能丢失。为了解决这个问题,可以使用新建bochsrc.txt文件来解决。效果相同。当然也有其他的办法来替代。
8 系统设计总结
经过一段时间的不懈努力,我终于完成了本次实验。我觉得本次实验对我的意义重大。它使我加深了对操作系统一些概念的理解;使我接触到了一些我以前没有接触到的知识、事务,如Geekos,Bochs,对Linux操作系统的学习也更加深入。并了解了gcc和make以及gdb的简单应用等。并对操作系统底层架构有了更进一步的认识。