本文正在参加「金石计划」
前言
工欲善其事,必先利其器。要想成功在一个不熟悉的领域开始探索,配好所需环境很重要。
虽然谁都想一劳永逸地配置好环境,但是奈何这项工作还是很考验一个人的经验和功底的。 笔者以下记录的是过程中为了解决某些问题而采用的方案,方案在当时是十分适用的,而后续由于进度需要我并没有继续跟进了,也欢迎采用了下文提到的解决方案的同学留言或者私信告诉我感想。因为我在折腾了好几天之后最终得出了这样一个结论:
要想兼容一个已经很老旧的系统(比如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-5.4版本对应的ubuntu16.04 blog.csdn.net/weixin_4372…
- 修改apt源配置文件,切换为ubuntu16.04对应的安装源 blog.csdn.net/weixin_4457… blog.csdn.net/qq_39779233…
笔者当时采用的是第二种方法。
具体步骤
- 确认当前的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一栏对应的序号并按下回车。
现在应该就成功了。
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
不了解也没关系,图形界面下并不是很需要用到它的命令。
一点遗憾
笔者曾经花了比较长的时间尝试在没有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/buildmake dependmake
如果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.huint_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,表示不进行任何优化即可。