【操作系统】GeekOS完成记录(一)相关环境配置

1,422 阅读8分钟

本文正在参加「金石计划」

前言

工欲善其事,必先利其器。要想成功在一个不熟悉的领域开始探索,配好所需环境很重要。

虽然谁都想一劳永逸地配置好环境,但是奈何这项工作还是很考验一个人的经验和功底的。 笔者以下记录的是过程中为了解决某些问题而采用的方案,方案在当时是十分适用的,而后续由于进度需要我并没有继续跟进了,也欢迎采用了下文提到的解决方案的同学留言或者私信告诉我感想。因为我在折腾了好几天之后最终得出了这样一个结论:

要想兼容一个已经很老旧的系统(比如20年前的GeekOS-0.3.0)、包括使用一个很老旧的模拟器去运行它(比如连umd最新的实验指南都已经推荐使用qemu而不是bochs),最好的办法不是修改各种最新的工具来对它修修补补,而是找一个和它同样老旧的工具来适配它。

因此,笔者最终采用的方案是:在电脑安装一个ubuntu9的带图形界面的虚拟机。

  • 其默认的gcc版本为3.4.6
  • nasm版本为2.05.01
  • bochs版本为2.3.7

有了如上配置,和项目代码及思路无关的报错问题可以减少99%。

然而总结经验的过程本身也是一个学习的过程,完成项目能让人在玩具层面了解操作系统;解决报错能让人在现实的系统层面了解操作系统。所以我还是决定把过程记录下来,供其他人参考。


适配低版本的GeekOS

完成项目遇到的第一个问题,就是所用的GeekOS版本太低,现在的编译器无法编译里面的代码,所以只能降级处理。

安装并切换至低版本的gcc

最初看指导资料里提到的gcc是4.x,需要修改geekos源码以完成适配;看往届学长在网上写的经验贴用的是5.x。

笔者当时使用的系统是ubuntu20.04,直接安装gcc-5会报错,查看了网上的解决方案,有两种:

笔者当时采用的是第二种方法。

具体步骤

  • 确认当前的gcc版本 gcc -v
  • 打开配置文件 vim /etc/apt/sources.list
  • 在文件末尾加入ubuntu16.04对应的版本号: deb https://mirrors.aliyun.com/ubuntu/ xenial main universe

温馨提示:

  • 关于vim操作:在vim中按shift+g或者G可以快速跳转至文件末尾(事实上这两个是一个意思,shitf+G输出的就是G);按o新起一行进入编辑模式;在putty中右键即可粘贴。
  • 关于各ubuntu源版本代号:
Ubuntu 22.04:jammy
Ubuntu 20.04:focal
Ubuntu 18.04:bionic
Ubuntu 16.04:xenial
  • 修改完后,更新一下apt源 apt-get update
  • 笔者没有遇到文章中说的更新源出错的问题,如果有,按照要求导入公钥即可: apt-key adv --keyserver keyserver.ubuntu.com --recv-keys <报错中提到的密钥串>

(看原文是有两个密钥串,然后各输入了一次)

  • 下载 apt install gcc-5

接下来准备切换版本

  • 查看已有的gcc列表 ls /usr/bin/gcc*
  • 给不同版本设置优先级。数字越大,优先级越高。 比如笔者是这么设置的:
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-5 30 --slave /usr/bin/g++ g++ /usr/bin/g++-5
 
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 20 --slave /usr/bin/g++ g++ /usr/bin/g++-7

由于优先级高的就是默认版本,所以设置好后按理说当前版本已经切换为gcc-5,可以输入gcc-v验证一下。

  • 如果还是不行,可以手动设置版本: update-alternatives --config gcc 然后输入对应版本前selection一栏对应的序号并按下回车。 image.png

现在应该就成功了。

nasm&&bochs安装

关于nasm

它是一个支持汇编和反汇编的工具,07年出版的指导手册里是这么写的,截止2023年,里面的很多内容还是有些参考意义的,追溯历史,才可以展望未来:

NASM是一个为可移植性与模块化而设计的一个80x86的汇编器,支持相当多的目 标文件格式。包括Linux和NetBSD/FreeBSD的a.out、ELF、COFF、微软16位和32位 的OBJ、还可以输出纯二进制代码文件。NASM的语法设计和Intel语法相似,比较简洁 易懂,支持Pentium、P6、MMX、SSE和SSE2指令集,NASM当初被设计出来的想法 从本质上讲,是因为当时没有一个好的免费的X86体系的汇编器可以使用,为了使开源 软件项目活动顺利开展,必须为用户提供一个好用的、免费的汇编器。

nasm安装

  • 现有的Linux发行版默认都已经安装了nasm。可以先nasm -v查看自己系统中是否已经存在nasm
  • 如果不存在,apt-get install nasm即可,软件源里默认会有的;
  • 实在没有,可以通过wget的方式去官网下

关于bochs

bochs是一个模拟器。

安装bochs

apt-get install nasm

了解基本命令

  • bochs --help image.png

不了解也没关系,图形界面下并不是很需要用到它的命令。

一点遗憾

笔者曾经花了比较长的时间尝试在没有GUI的linux服务器上跑bochs,当时翻看文档发现可以配置成nogui,但是修改了配置文件并用系统软件包安装的bochs却显示没有这个选项;后面尝试过通过编译源码(修改代码再编译)的方式装bochs,但是由于PIE设置的原因一直没办法顺利完成(这中间多次修改了bochs的Makefile文件,还是不行),由于时间关系还是放弃折腾了,因为最初只是图方便才这样做,但是反而更加不方便了,就很没必要。要将主要精力放在主要任务上。

安装GeekOS

笔者注:本来去官网wget下来是最快的,但我前面已经在本机上下好了老师提供的GeekOS-0.3,就想着远程传输上去就算了。

远程传输文件基于一些协议来进行,如ftp/scp/sftp,有很多工具都能很方便做到这一点,比如putty自带的pscp(一次只能传输一个文件,传输完毕后终止会话)、psftp(交互式,支持多个文件);支持windows系统的winscp(实际上它也是借助于putty,因为笔者发现利用winscp连接过之后,putty的连接信息也会多一行,而且如果是密钥连接,winscp会默认将文件生成为putty的pem格式)。

由于时间关系,笔者用电脑之前已经装了的winscp,鼠标点一点完成主机连接并传输文件。这里不再赘述。

  • 解压:unzip geekos-0.3.0.zip

熟悉项目结构

一点题外话:笔者去搜了一下,2016和2017年以前,UMD没有在网上发布关于GeekOS的word文档,像这个0.3版本的一些参考资料可以在doc目录下找到。

16年以后出现的pdf版本中,使用的工具是qemu和git。

  • script目录下有两个脚本,其中的startProject主要用于将之前project的内容整合进新project中(因为新project可能会用到原project写好的一些文件)

一点题外话: 脚本源码是这么写的:

	# If it exists in the new project but not the previous project,
	# just copy it.
	if (!$prevFileExists) {
		my $rc = system("cp $newFile $outFile")/256;
		if ($rc != 0) {
			print STDERR "Error: Could not copy $newFile to $outFile\n";
			exit 1;
		}
	}

	# If it exists in the previous project but not the new one (i.e.,
	# the student added it somewhere along the way), just copy it.
	elsif (!$newFileExists) {
		my $rc = system("cp $prevFile $outFile")/256;
		if ($rc != 0) {
			print STDERR "Error: Could not copy $prevFile to $outFile\n";
			exit 1;
		}
	}

	# File exists in both previous project and new master project.
	# Use diff3 to generate it.
	else {
		my $prevMasterFile = "$masterDir/$prevProject/$file";

		my $diff3Command = "diff3 -L 'Your version of $file' -L 'Master version of $file from $prevProject' -L 'Master version of $file from $projectName' -E -am $prevFile $prevMasterFile $newFile > $outFile";
		#print "$diff3Command\n";
		my $rc = system($diff3Command)/256;
		if ($rc == 1) {
			print STDERR "Warning: Conflicts detected in merge of file $file\n";
		} elsif ($rc != 0) {
			print STDERR "Error: Could not run diff3 to generate $outFile\n";
			exit 1;
		}
	}

里面用到了diff3这个工具,之前笔者使用过的git mergetool也用到了这个机制。

不过现在并不用这么麻烦,完全可以像mit6.s081的xv6一样fetch最新代码再切换分支就行。


  • 重点关注src目录。

src下一共有7个project,目录结构大致相同,以project0为例:

.
├── build //编译相关
│   ├── common
│   ├── geekos
│   ├── libc
│   ├── Makefile  //在模拟器中启动GeekOS及其用户程序
│   ├── tools
│   └── user
├── COPYING
├── include
│   ├── geekos //内核态头文件
│   └── libc
├── LICENSE-klibc
├── scripts  //perl脚本,有和编译相关的内容
└── src //源码
    ├── common
    ├── geekos //内核态文件
    ├── libc //系统调用的c实体函数
    ├── tools //最新版的geekos是包含用于支持qemu的内容,0.3版的没有
    └── user //用户态文件,需要我们补充 

quick start

make

  • cd <geekos存放路径>/geekos-0.3.0/src/project0/build
  • make depend
  • make

如果gcc的版本不匹配,make的过程会有很多报错。比如:

  • error:‘packed’attribute ignored for filed of type 'uchar_t' 解决方案:打开Makefile文件,将CC_GENERAL_OPTS := $(GENERAL_OPTS) -Werror中的-Werror修改为-fno-stack-protector,意思是强制gcc不进行栈检查。 此外,需要修改的源码还有:
  • segment.h uint_t reserved : 0 PACKED ;
  • floppy.c
static int Floppy_Get_Num_Blocks(struct Block_Device *dev)
{
    struct Floppy_Drive *drive=NULL;
}

排除掉编译器原因,如果是自身的代码导致的报错:

  • 如果感觉是很小的一部分写错了,可以修改相应位置,然后再次make;
  • 如果感觉前后的make之间相互影响,可以用make clean,然后再次make。

使用bochs

修改Bochs启动文件

# You will need to edit these lines to reflect your system.
vgaromimage: /export/home/daveho/linux/bochs-2.0.2/share/bochs/VGABIOS-lgpl-latest
romimage: file=/export/home/daveho/linux/bochs-2.0.2/share/bochs/BIOS-bochs-latest, address=0xf0000

替换上述两行的安装目录版本参数为自己的bochs路径。 如果bochs是通过命令默认安装的,直接注释掉这两行就可以了,系统会默认寻找$BXSHARE下的相关文件,而这个$BXSHARE就是安装路径

  • 要找到bochs的位置可以用whereis命令。
  • 要获取版本可直接输入bochs,第一行就是相关信息

在项目0中:只需要如下配置(多写了启动bochs的时候就会报错说找不到):

megs: 8
boot: a
floppya: 1_44=fd.img, status=inserted
log: ./bochs.out

项目1之后,还需要用到ata串口驱动器:

ata0-master: type=disk, path=diskc.img, mode=flat, cylinders=40, heads=8, spt=64

启动bochs

在projectx下的build目录下输入bochs,它会自动匹配到该目录下的.bochsrc配置文件从而启动。

  • 如果有exception 13的错误,是因为高版本的编译器对代码进行了优化导致不兼容。 在Makefile文件中,将CC_GENERAL_OPTS := $(GENERAL_OPTS)后加个-O0,表示不进行任何优化即可。