Greekos操作系统实验Word格式.docx
《Greekos操作系统实验Word格式.docx》由会员分享,可在线阅读,更多相关《Greekos操作系统实验Word格式.docx(27页珍藏版)》请在冰豆网上搜索。
为了实现与Windows下的文件进行共享,需要安装VMwareTools软件包。
在运行的时候需要bochspc模拟器来模拟操作系统,所以也需要安装bochspc模拟器。
2.1在虚拟机上安装Linux
本次环境的搭建是采用在Windows环境下先安装一个PC虚拟机,然后在虚拟机上安装Linux操作系统。
本次实验选择安装的虚拟机软件VMware,安装过程则为首先在相应的网站下载此安装软件,然后其根据提示安装。
安装完成后就会在桌面上显示一个虚拟机图标VMware-workstation。
VMware安装完成后,可以开始建立虚拟机,每新建一个虚拟机都会要求建立一个配置文件,这个配置文件相当于电脑中的硬件配置表,用户可以在配置文件中决定虚拟机的硬盘如何配置,内在多大,准备运行哪种操作系统,是否有网络等。
其中中安装的过程中要选取实验需要的相应组件进行安装,其中本次实验必须需要的组件是:
AWK、Diff3、Egrep、gcc、GNUbinutils、GNUMake、Perl、NASM。
2.2安装VMwareTools
在虚拟机上安装VMwareTools,就相当于给Linux安装各种驱动程序。
此步的主要目的就是为了以后的工程运行时能够实现与Windows下的文件进行共享,因为bochsPC模拟器要在Windows下运行,所以这里的文件就只能通过这一步的共享,从而达到在Linux下工程运行后得到的build下的镜像文件替代源文件,从而使得bochsPC模拟器能够得到所需的镜像文件。
2.3安装BochsPC模拟器
BochsPC模拟器:
用来运行GeekOS系统。
安装此软件只需设定好想安装到的文件目录后一直点下一步就可以安装成功。
最后设置bochsrc.txt文件。
根据实验的需要,一般只需要修改以下几项:
(1)vgaromimage:
$BXSHARE/VGABIOS-lgpl-latest
(2)romimage:
file=$BXSHARE/BIOS-bochs-latest,address=0xf0000
(3)floppya:
1_44=fdx.img,status=insertedboot:
floppy
(4)做Project1的时候,需要添加一个磁盘镜像
ata0-master:
type=disk,mode=flat,path=diskx.img,cylinders=615,heads=6,spt=17配置完bochsrc.txt以后,而且有了从工程生成的操作系统Geekos就可以用bochs软件模拟了。
到bochs的安装目录下,输入bochs命令,选择6开始模拟。
如果你的操作系统编译成功,就可以得到想要的结果。
3.项目具体实现
3.1项目0
本项目主要目的是要熟悉GeekOS的项目编译、调试和运行环境,掌握GeekOS运行工作过程。
3.1.1项目设计要求
(1)搭建GeekOS的编译和调试平台,掌握GeekOS的内核进程工作原理。
(2)熟悉键盘操作函数,编程实现一个内核进程。
该进程的功能是:
接收键盘输入的字符并显示到屏幕上,当输入Ctrl+D时,结、束进程的运行。
3.1.2项目设计原理
在这个项目里面主要了解两部分的内容:
内核线程和键盘处理,相应的文件是thread.c和keyboard.c。
Geekos系统的默认内核只支持内核态的线程,在系统初始化的时候,main函数分别执行了4个内核函数,一个内核函数负责软驱中断,一个复杂键盘中断,还有两个负责进程调度。
函数Start_Kernel_Thread()其可以生成一个内核线程:
1.内核线程结构的定义如下:
structKernel_Thread{
unsignedlongesp;
volatileunsignedlongnumTicks;
intpriority;
DEFINE_LINK(Thread_Queue,Kernel_Thread);
void*stackPage;
structUser_Context*userContext;
structKernel_Thread*owner;
intrefCount;
Booleanalive;
structMutexjoinLock;
structConditionjoinCond;
};
esp字段用来存放一个线程挂起的堆栈指针;
stackPage字段指向内核线程的堆栈页面
numTicks和priority分别被调度程序用来实现基于先占权和基于优先权的时间片调度。
DEFINE_LINK宏定义一个内核线程在线程队列上时的前一个和后一个字段。
userContext字段如果不为空,则指向一个线程用户环境,它是一个允许线程执行用户模式的代码和数据的组合段。
内核线程有两种方式创建。
在内核里独立运行的线程可通过Start_Kernel_Thread()函数来创建,该函数通过一个指针指向一个执行线程体的启动函数。
线程所执行的用户模式的程序由Start_User_Thread()函数创建,并且用一指针指向一个用户环境和用户环境内存中代码入口点的地址。
调用Exit()函数销毁内核线程。
入口参数分别为:
函数地址,函数参数(无参数就写0),优先级设定,线程属性(false为内核线程,true为用户线程),返回值Mythread的数据类型是staticstructKernel_Thread*thread
2.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();
//使能中断
3.Geekos处理键盘代码
在keyboard.c里面提供了一个功用函数KeycodeWait_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
3.1.3项目设计的具体实现
projcet0项目主要是对main.c文件中的键盘相应的实现。
我们创建函数keyboard()。
下面是具体的代码。
voidkeyboard(ulong_targ)
{
Keycodekey;
Print("
PleaseEnterthecharacters.press'
Ctrl+d'
toexit\n\n"
);
while(true)
{
Set_Current_Attr(ATTRIB(BLACK,RED|BRIGHT));
key=Wait_For_Key();
if(key==(KEY_CTRL_FLAG+'
d'
))
\n\nKeyEnd\n"
break;
}
if(!
(key&
KEY_RELEASE_FLAG)&
&
!
KEY_SPECIAL_FLAG))
%c"
key);
}
voidMain(structBoot_Info*bootInfo)
Init_BSS();
Init_Screen();
Init_Mem(bootInfo);
Init_CRC32();
Init_TSS();
Init_Interrupts();
Init_Scheduler();
Init_Traps();
Init_Timer();
Init_Keyboard();
Set_Current_Attr(ATTRIB(BLACK,GREEN|BRIGHT));
\n\nWelcometosutingting'
sGeekOS!
\n\n"
Start_Kernel_Thread(keyboard,0,5,1);
Set_Current_Attr(ATTRIB(BLACK,GRAY));
TODO("
Startakernelthreadtoechopressedkeysandprintcounts"
Exit(0);
3.1.4系统编译运行的原理及结果
I.编译源代码
在linux的终端中:
(1)输入:
cd+空格+路径(project0文件下的build文件所在的路径)
(2)终端进入build目录
$makedepend
$make
如果没有问题就会自动生成一个镜像文件fd.img。
II修改bochsrc.txt配置文件
其具体的文件bochsrc.txt内容如下:
#配置模拟器的BIOS和显示系统的BIOS文件
vgaromimage:
romimage:
#配置模拟器内存大小
megs:
8
#配置模拟器从软盘引导系统
boot:
a
#这是对模拟器硬盘的描述,其中disk.img是硬盘的映像文件,将其注释掉,因为项目0
#不需要硬盘。
#软盘A的描述,其中fd.img为软盘映像文件
floppya:
1_44=fd.img,status=inserted
#floppya:
1_44=fd_aug.img,status=inserted
#这是配置模拟器的系统文件
keyboard_serial_delay:
200
floppy_command_delay:
500
vga_update_interval:
300000
ips:
1000000
mouse:
enabled=0
private_colormap:
i440fxsupport:
#配置模拟器的日志文件
log:
./bochs.out
III.运行bochs
终端运行bochs选择6即的运行结果如下图1所示:
图1project0的运行结果
3.2项目1
本项目主要是为了让学生熟悉ELF文件格式,了解Geekos系统如何将ELF
格式的用户可执行程式装入到内存,建立内核进程并运行的实现技术。
3.2.1项目设计要求
分析ELF格式的可执行文件(包括分析得出ELF文件头、程序头,获取可执行文件的长度,代码段、数据段等信息),并填充Exe_Format数据结构中的域值。
(1)修改/geekos/elf.c文件:
在函数Parse_ELF_Executable()中添加代码,分析ELF格式的可执行文件(包括分析得出ELF文件头、程序头,获取可执行文件长度,代码段、数据段等信息),并填充Exe_Format数据结构中的域值。
(2)在Linux环境下编译系统得到GeekOS镜像文件。
(3)编写一个相应的bochs配置文件。
(4)在bochs中运行GeekOS系统显示结果。
3.2.2项目设计原理
项目的主要内容是在文件elf.c中函数Parse_ELF_Executable()添加代码,实现对ELF文件的解释,装入到内存运行,得到指定的结果.主要是分析ELF文件。
为以后的工程执行应用程序做准备,需要理解的是ELF的一下原理性。
需要关注以下几个数据结构:
具体代码在\project1include\geekos\elf.h中:
typedefstruct{
unsignedcharident[16];
unsignedshorttype;
unsignedshortmachine;
unsignedintversion;
unsignedintentry;
unsignedintphoff;
//programheader偏移量
unsignedintsphoff;
//sectionheader的偏移
unsignedintflags;
//指示具体的进程
unsignedshortehsize;
//elf头部的大小
unsignedshortphentsize;
//programheader的大小
unsignedshortphnum;
//programheader的个数
unsignedshortshentsize;
unsignedshortshnum;
unsignedshortshstrndx;
}elfHeader;
structExe_Format{
structExe_SegmentsegmentList[EXE_MAX_SEGMENTS];
//段的定义
intnumSegments;
//可执行文件中段的个数
ulong_tentryAddr;
//代码入口
};
structExe_Segment{
ulong_toffsetInFile;
//段在可执行文件中的偏移
ulong_tlengthInFile;
//段在可执行文件中的长度
ulong_tstartAddress;
//段在内存中的起始地址
ulong_tsizeInMemory;
//段在内存中的大小
intprotFlags;
//VM保护标志
typedefstruct{
unsignedinttype;
unsignedintoffset;
unsignedintvaddr;
unsignedintpaddr;
unsignedintfileSize;
unsignedintmemSize;
unsignedintalignment;
}programHeader;
offset表示该成员给出了该段的驻留位置相对于文件开始处的偏移。
fileSize表示该成员给出了文件映像中该段的字节数;
它可能是0。
memSize表示该成员给出了内存映像中该段的字节数;
vaddr表示该成员给出了该段在内存中的首字节地址。
flags表示该成员给出了和该段相关的标志。
这是编程实现过程主要部分,对linux下ELF有一定了解之后,就能顺利的完成工程1.
3.2.3项目设计的具体实现
下面是project1\src\geekos下的elf.c中的代码。
#include<
geekos/errno.h>
geekos/kassert.h>
geekos/ktypes.h>
geekos/screen.h>
/*fordebugPrint()statements*/
geekos/pfat.h>
geekos/malloc.h>
geekos/string.h>
geekos/elf.h>
intParse_ELF_Executable(char*exeFileData,ulong_texeFileLength,
structExe_Format*exeFormat)
inti;
elfHeader*head=(elfHeader*)exeFileData;
programHeader*proHeader=(programHeader*)(exeFileData+head->
phoff);
Set_Current_Attr(ATTRIB(BLACK,BLUE|BRIGHT));
ident=%s,"
head->
ident);
type=%d,"
type);
machine=%d,"
machine);
version=%d\n"
version);
entry=%d,"
entry);
phoff=%d,"
sphoff=%d,"
sphoff);
flags=%d\n"
flags);
ehsize=%d,"
ehsize);
phentsize=%d,"
phentsize);
phnum=%d,"
phnum);
shentsize=%d\n"
shentsize);
shnum=%d,"
shnum);
shstrndx=%d\n"
shstrndx);
proHeader->
offset=%d,"
offset);
vaddr=%d,"
vaddr);
paddr=%d\n"
paddr);
fileSize=%d,"
fileSize);
memSize=%d,"
memSize);
flags=%d,"
alignment=%d\n"
alignment);
for(i=0;
i<
head->
phnum;
i++)//programheadertablenumber
exeFormat->
segmentList[i].offsetInFile=proHeader->
offset;
segmentList[i].lengthInFile=proHeader->
fileSize;
segmentList[i].startAddress=proHeader->
vaddr;
segmentList[i].sizeInMemory=proHeader->
memSize;
segmentList[i].protFlags=proHeader->
flags;
proHeader++;
numSegments=head->
entryAddr=head->
entry;
return0;
3.2.4系统编译运行的原理及结果
cd+空格+路径(project1文件下的build文