官方网站:GeekOS 点击右边的“download”,然后点击Download Latest Version下载并解压。
解释:笔者通过多次测试发现,编译的geekos-0.3版本非常古老,直接编译会出现一堆错误,所以使用同样比较古老的gcc-4.8来进行编译。 (1) 使用gcc -v查看gcc版本 (2) 通过sudo apt-get install gcc-4.8安装,然后输入ls /usr/bin/gcc* -l来查看已安装版本。 (3) 由于两个版本的gcc同时存在,故需要设定优先级。
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 100 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 100设定指令选择不同版本的gcc
sudo update-alternatives --config gcc(4) 选择后查看gcc版本:gcc -v
(1) 进入geekos-0.3.0/src/project0/build目录 (2) 首先使用make clean清除上次的make命令所产生的object文件(后缀为“.o”的文件)及可执行文件。 (3) 开始编译
make clean make depend make注意:每次修改保存后必须make clean,每修改一次都要!!!
error: Makefile:172: recipe for target ‘geekos/gdt.o’ failed 解决方案: 因为Makefile中默认的编译选项过于严格,把警告都当成错误来看待。在Makefile的第149行删除-Werror:
error: ld: i386 架构于输入文件 geekos/lowlevel.o 与 i386:x86-64 输出不兼容 common/fmtout.o:在函数‘Format_Output’中: fmtout.c:(.text+0xa16):对‘__stack_chk_fail’未定义的引用 Makefile:223: recipe for target ‘geekos/kernel.exe’ failed make: *** [geekos/kernel.exe] Error 1 解决方案: 在64位机器的默认配置下导致的,我们可以强制增加编译选项-m32和链接选项-m elf_i386,打开Makefile,将第100、106、109行修改如下:
# Target C compiler. gcc 2.95.2 or later should work. TARGET_CC := $(TARGET_CC_PREFIX)gcc -m32 # Host C compiler. This is used to compile programs to execute on # the host platform, not the target (x86) platform. On x86/ELF # systems, such as Linux and FreeBSD, it can generally be the same # as the target C compiler. HOST_CC := gcc -m32 # Target linker. GNU ld is probably to only one that will work. TARGET_LD := $(TARGET_CC_PREFIX)ld -m elf_i386error: common/fmtout.o:在函数‘Format_Output’中: fmtout.c:(.text+0x836):对‘__stack_chk_fail’未定义的引用 Makefile:223: recipe for target ‘geekos/kernel.exe’ failed make: *** [geekos/kernel.exe] Error 1 yadajin@ubuntu:~/Downloads/geekos-0.3. 解决方案: 因为gcc在编译时开启了栈保护,所以要关闭栈保护。 修改第148行。
GENERAL_OPTS := -O -Wall -fno-stack-protector $(EXTRA_C_OPTS)会在build目录下生成一个镜像文件fd.img和bochs模拟器的配置文件.bochsrc。
在终端输入bochs命令 如果新弹出的bochs窗口黑屏,可能是进入了调试模式,在终端中输入c回车即可。
如果出现此情况,估计是nasm版本高了,试试2.00的。去官网下载2.00版本即可。
./configure make sudo make install此虚拟机镜像环境已搭建好,可直接运行例程。(内含百度云链接)
通过对比Ubuntu 16版本和Ubuntu 9版本,发现GeekOS在Ubuntu 9版本里bug较少,毕竟GeekOS是个很古老的东西,从官网上看最近的一次维护是2008年,然而现在是2020年,可见相隔甚远。所以笔者建议做GeekOS课设时还是用Ubuntu 9版本的吧,这样省去很多系统环境问题的麻烦。当然如果你在较新的Ubuntu版本完成了GeekOS课设,欢迎评论区留言,大家交流学习。
1.GeekOS系统内核是如何编译?
答:需提前安装GNU gcc编译器,用来编译C语言程序代码,NASM汇编器用来编译汇编语言程序代码。编译GeekOS源代码通过build目录下的Makefile文件来编译,其中已经写好了编译规则,使用make命令编译。(make depend扫描目录,判断依赖关系)
2.GeekOS系统内核编译的结果是什么?
答:GeekOS内核编译后,在build目录下会生成一个软盘镜像文件fd.img。
3.如何设置运行GeekOS系统的计算机环境?
答:安装Bochs PC模拟器,用来模拟运行GeekOS系统。
4.如何运行GeekOS系统内核?
答:使用Bochs模拟器模拟来运行Geekos系统内核,需要编写配置文件.bochsrc配置相应信息后使用bochs命令启动Bochs模拟器运行GeekOS编译生成的镜像(fd.img)。
5.GeekOS系统是如何创建键盘管理进程?
答:Init_Keyboard()函数:初始化键盘输入,包括初始化键盘缓冲区队列,初始化键盘中断处理函数,最后开启键盘中断。
void Init_Keyboard(void) { ushort_t irqMask; Print("Initializing keyboard...\n"); s_shiftState = 0;/* 开始时不启用shift键 */ s_queueHead = s_queueTail = 0;/* 初始缓冲区为空 */ Install_IRQ(KB_IRQ, Keyboard_Interrupt_Handler);/* 安装中断处理程序 */ irqMask = Get_IRQ_Mask();/* 启用IRQ1(键盘) */ irqMask &= ~(1 << KB_IRQ); Set_IRQ_Mask(irqMask); }6.简述计算机系统的启动流程 答:
在Bochs开始运行系统后,首先会自动检测启动设备。设备检测无异常往下执行,因为软盘首扇区最后一个字在编译时是写入55AA数据,而Bochs被配置为从软盘启动,这样Bochs得以成功地检测到GeekOS的启动软盘.之后Bochs就会像一台真正的计算机一样。导入软盘的首扇区数据到从内存地址0x7c00开始的一块内存区,之后跳转到这个地址,开始执行这段首扇区内的程序代码.首扇区内的代码是由位于/src/geekos目录中的fd_boot.asm编译生成的引导程序.这段汇编程序完成搜索并装载软盘中的GeekOS内核二进制文件的功能在装载完毕后,装载程序执行段间跳转,转入程序Setup(/src/geekos目录中的setup.asm编译生成)继续执行.Setup程序完成装载临时GDT,IDT描述符,打开A20地址线,初始化PIC中断控制器,最后由实模式跳入保护模式.完成了实模式向保护模式的转换之后,Setup跳转到内核ENTRY_POINT入口点.至此,GeekOS的引导过程结束,内核初始化过程开始。GeekOS的内核入口点ENTRY_POINT指向的是内核Main函数的函数入口,在编译时完成对ENTRY_POINT的初始化.Main函数在src/src/geekos/main.c中实现.Main函数通过调用内核各模块的初始化函数来完成系统内核的初始化。1.ELF文件格式包括什么内容?
答:ELF文件由4部分组成,分别是ELF头(ELF header)、程序头部表(Program header table)、节区(Section)和节头部表(Section header table)。
2.如何由ELF文件建立一个可运行的进程单位?
答:ELF文件是保存在GeekOS的PFAT文件系统中,首先需要将文件读入内存缓冲区,解析程序段和数据段等将其装入内存,之后建立进程执行装入内存相应的代码。
3.在GeekOS系统中建立一个内核级线程应该建立什么相应实体单元?
答:Kernel_Thread实体单元。
4.在GeekOS系统内核中,如何运行建立好的线程?
答:GeekOS操作系统为不同状态下的进程准备了不同的进程队列:就绪队列s_runQueue,等待队列s_reaperWaitQueue,销毁队列s_graveyardQueue,而运行态的只有一个g_currentThread指针指向哪个进程哪个进程就处于运行态。系统启动之后,会从Entry入口执行Main函数,执行到Init_Scheduler()函数时会建立3个最开始的进程Main、Idle和Reaper,同时调度Main。Idle在就绪队列队尾,当无进程可调度时Idle就会被调度并一直运行Yield()函数检查是否有可调度进程并让步。所以新建的线程加入到就绪队列就可以通过相应的调度算法被调度运行。
1.GeekOS 系统启动后运行的第一个用户级线程是什么?
答:shell
2.GeekOS 系统中,内核级线程和用户级线程有什么区别?
答:
进程结构不同。内核进程没有User_Context 结构体,只有Kernel_Thread结构体。而用户进程既拥有Kernel_Thread结构体,也拥有User_Context 结构体。User_Context 结构体保存了用户栈地址、段描述符、段选择子、用户程序入口地址等内容。栈空间不同。每个进程都有自己的栈,内核进程有1个栈,内核栈;用户有两个栈空间,内核栈和用户栈空间。用户栈的空间指向用户地址空间,内核栈的空间指向内核地址空间。当进程在用户态运行(Ring3)时,CPU栈指针寄存器指向的 用户栈地址,使用用户栈。当进程运行在内核态时(Ring0),CPU栈指针寄存器指向的是内核栈空间地址,使用的是内核栈。其中用户栈地址保存在User_Context结构体中,内核栈地址保存在Kernel_Thread结构体中。3.什么是全局描述表?什么是局部描述表?
答:
全局描述表GDT。在整个系统中,全局描述符表GDT只有一张(一个处理器对应一个GDT),GDT可以被放在内存的任何位置,但CPU必须知道GDT的入口,也就是基地址放在哪里,Intel的设计者门提供了一个寄存器GDTR用来存放GDT的入口地址,程序员将GDT设定在内存中某个位置之后,可以通过LGDT指令将GDT的入口地址装入此积存器,从此以后,CPU就根据此寄存器中的内容作为GDT的入口来访问GDT了。GDTR中存放的是GDT在内存中的基地址和其表长界限。局部描述表LDT。局部描述符表是保护模式下存储器寻址的一种数据表,它包含了与某个任务相关联的段描述符,在设计操作系统时,通常每个任务有一个独立的LDT。LDT提供了将一任务的代码段、数据段与操作系统的其余部分相隔离的机制。4.在GeekOS系统内核中,如何由ELF文件创建一个用户级线程?
答:用户级线程和内核级线程可以通过Kernel_Thread对象的userContext指针是否为空判断。与内核级线程相比创建用户级进程需要有User_Context对象、用户栈空间和用户地址空间。通过读取解析ELF文件得到可执行文件数据段、代码段等信息存入用户地址空间,之后完成User_Context对象和用户栈空间相关属性的初始化,得到的User_Context对象由Kernel_Thread对象的userContext指针指向。从而创建了用户级线程。
5.在GeekOS系统内核中,如何选择一个建立好的用户级线程来运行?
答:加入准备运行队列,等待系统调度运行。
1.GeekOS 系统原始的线程调度算法是什么?
答:轮询调度算法
2.在GeekOS 系统中,如何实现多级反馈队列调度算法?
答:先要修改准备运行队列的数量,将Idle进程加入到最后一个队列尾,然后修改Make_Runnable()函数实现将时间片用完的进程根据进程队列索引值加入相应的准备运行队列,修改Get_Next_Runnable()函数完成多级反馈队列的调度策略。
3.在GeekOS 系统中,如何创建一个信号量?
答:首先需要定义信号量结构体,以及信号量队列。然后通过Sys_CreateSemaphore()函数创建信号量,Sys_CreateSemaphore()函数功能实现,首先通过传入的参数获取信号量的名称,通过名称查找信号量是否在信号量队列,没有则创建信号量并加入队列。然后将进程注册加入到信号量的注册队列。这样进程就创建了一个信号量。
4.在GeekOS系统中,如何通过信号量来实现进程同步?
答:不同进程注册了同一个信号量之后,便可以通过PV操作实现进程间的同步关系,信号量的值只能由PV操作改变。P操作用于获得信号量,若信号量值小于等于0,则进程阻塞,若信号量值大于0,则信号量值减1,程序继续运行。V操作用于释放信号量,将信号量的值加1,若有进程等待该信号量,则唤醒等待队列首进程。具体的实现以生产消费者问题为例,可以是通过Shell执行一个用户程序,这个用户程序创建两个子进程执行另外两个用户程序,都注册名称相同的信号量,一个执行V操作作为生产者,另一个执行P操作作为消费者,这样就实现了一个简单的使用信号量PV操作解决进程同步的例子。
如果对你有帮助,点赞收藏再走呗。