熟悉ELF文件格式,了解GeekOS系统如何将ELF格式的可执行程序装入到内存,建立内核进程并运行的实现技术。
1) 修改/geekos/elf.c文件:在函数Parse_ELF_Executable( )中添加代码,分析ELF格式的可执行文件(包括分析得出ELF文件头、程序头,获取可执行文件长度,代码段、数据段等信息),并填充Exe_Format数据结构中的域值。
2) 在Linux环境下编译系统得到GeekOS镜像文件。
3) 编写一个相应的bochs配置文件。
4) 在bochs中运行GeekOS系统显示结果。
*Create_Thread函数主要功能为通过Alloc_Page函数为Kernel_Thread结构体和内核进程栈区分配内存空间,并通过Init_Thread函数初始化Kernel_Thread结构体。最后将该进程结构体通过Add_To_Back_Of_All_Thread_List函数加入进程队列末尾。
*Setup_Kernel_Thread函数主要是初始化进程栈区,包括参数地址、返回地址、入口地址、寄存器值等。
*Make_Runnable_Atomic函数将该进程设置为就绪态,加入等待执行队列。
struct Exe_Format{
struct Exe_Segment segmentList[EXE_MAX_SEGMENTS];
int numSegments; //定义了ELF文件中段的个数
ulong_t entryAddr; //代码入口地址
};
struct Exe_Segment{
ulong_t offsetInFile; //段在可执行文件中的偏移值
ulong_t lengthInFile; //段在可执行文件中的长度
ulong_t startAddress; //段在内存中的起始地址
ulong_t sizeInMemory; //段在内存中的大小
int protFlags; //保护标志
};
由于系统已经实现了内核的所有函数,本项目设计只需完成Parse_ELF_Executable即可。Parse_ELF_Excutable函数声明为:
int Parse_ELF_Executable(char *exeFileData, ulong_t exeFileLength,struct Exe_Format *exeFormat)
参数:exeFileData——已装入内存的可执行文件所占用空间的起始地址
exeFileLength——可执行文件长度
exeFormat——保存分析得到的elf文件信息的结构体指针
根据ELF文件格式,用户可以从exeFileData指向的内容中得到ELF文件头,继续分析可以得到程序头,程序代码段等信息。
在项目1的./src/user目录下有一个a.c文件,编译GeekOS后(即make后)可以得到可执行程序a.exe,并写进PFAT文件系统,路径为c/a.exe。项目将此作为待装入的可执行文件,启动Bochs运行a.exe。
在a.c文件中,既有全局变量也有局部变量,这里还要注意一下局部变量如何输出。
cd ~/geekos-0.3.0/src/project1/build/
gedit .bochsrc
megs: 8
boot: a
floppya: 1_44=fd.img, status=inserted
Log: ./bochs.out
ata0-master: type=disk, path=diskc.img, mode=flat, cylinders=40, heads=8, spt=64
int Parse_ELF_Executable(char *exeFileData, ulong_t exeFileLength,struct Exe_Format *exeFormat)
{
//利用ELF头部结构体指向可执行文件头部,便于获取相关信息
elfHeader *ehdr = (elfHeader*)exeFileData;
//段的个数
exeFormat->numSegments = ehdr->phnum;
//代码入口地址
exeFormat->entryAddr = ehdr->entry;
//获取头部表在文件中的位置,便于读取信息
programHeader *phdr = (programHeader*)(exeFileData + ehdr->phoff);
//填充Exe_Segment
unsigned int i;
for(i = 0; i < exeFormat->numSegments; i++, phdr++)
{
struct Exe_Segment *segment = &exeFormat->segmentList[i];
//获取该段在文件中的偏移量*
segment->offsetInFile = phdr->offset;
//获取该段的数据在文件中的长度
segment->lengthInFile = phdr->fileSize;
//获取该段在用户内存中的起始地址
segment->startAddress = phdr->vaddr;
//获取该段在内存中的大小
segment->sizeInMemory = phdr->memSize;
//获取该段的保护标志位
segment->protFlags = phdr->flags;
}
return 0;
}
static void Printrap_Handler( struct Interrupt_State* state )
{
char *msg;
/* 此处修改以下内容以正确显示 a.c 中的局部变量 */
if (state->eax <= virtSize)
msg = (char *)virtSpace + state->eax;
else
msg = (char *)state->eax;
print(msg);
g_needReschedule = true;
return;
}
如图所示,运行后,屏幕显示a.c文件中的输出内容以及spawner函数中的输出内容,项目一设计完成。
附完整代码下载链接:GeekOS课程设计-project1-CSDN下载 http://download.csdn.net/download/qq_35008279/10190758