使用 Docker 配置上古 GCC-3.4 环境并编译 Linux-0.11 源码

Debug your app, not your environment.

我非常喜欢Docker的这句slogan

起因

哈尔滨工业大学-李治军《操作系统》课程提供了围绕Linux 0.11源码的一系列工具包,并以此作为实验环境进行教学。实验楼作为一个收费的计算机专业实验平台,配备了本课程的配套实验——但是免费用户每次只能使用虚拟机1小时,过时会重置系统,十分不方便。因此我选择在自己的服务器上搭建实验环境。

在轻车熟路地解压,编译后,我发现了问题所在:Makefile中写死了CC,规定版本为GCC-3.4,这可就麻烦了。

GCC-3.4就如同Linux 0.11一样,都是非常老的上古版本,软件源里早就不存在这么一号编译器了,于是我尝试了如下方案:

  • 修改Makefile,去除版本限制——显然,编译失败
  • 尝试从源安装GCC-3.4——但是源(ubuntu bionic@TUNA)根本就没有GCC-3.4
  • 去Ubuntu官方的Old-Release手动拉取deb包安装,然后建立软链接,修改变量优先级——工具链的相互依赖关系难以解决,一不留神就会把系统默认的编译环境搞乱了。当然,还是以失败告终,并且这些上古工具安装到系统的不知道什么地方后,我怕会留下什么意料之外的后遗症,于是cp了个人文件后,去控制台把服务器重置了

面对着重置后的系统,我突然一拍脑袋,Docker不就是为这种情况准备的吗?

Ubuntu x Docker

网上提供的方法大多是CentOS如何安装Docker,而关于Ubuntu的信息多为过时的,这里简单记录一下Docker的安装过程:

1
2
# 官方一键安装脚本,国外源速度可能较慢
curl -sSL https://get.docker.com | sudo bash

或者使用TUNA提供的镜像安装流程(Community Edition):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 如果你过去安装过 docker,先删掉
sudo apt-get remove docker docker-engine docker.io

# 安装依赖
sudo apt-get install apt-transport-https ca-certificates curl gnupg2 software-properties-common

# 信任 Docker 的 GPG 公钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

# 对于 amd64 架构的计算机,添加软件仓库
sudo add-apt-repository \
"deb [arch=amd64] https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu \
$(lsb_release -cs) \
stable"

# 如果你是树莓派或其它ARM架构计算机,请运行
echo "deb [arch=armhf] https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu \
$(lsb_release -cs) stable" | \
sudo tee /etc/apt/sources.list.d/docker.list

# 最后安装
sudo apt-get update
sudo apt-get install docker-ce

阿里云容器镜像服务 ACR 提供了官方的镜像站点,从而加速官方镜像的下载,该服务需要登录申请自己独一无二的镜像仓库:

  1. 登录阿里云镜像控制台
  2. 选择镜像中心->镜像加速器,即可获取专属镜像地址
1
2
3
4
5
6
7
8
9
#修改daemon配置文件/etc/docker/daemon.json,没有就新建
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://专属代码.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

Docker的运行需要root权限,如果不想每次都输入sudo的话,新建docker用户组并将当前用户添加进去

1
2
sudo groupadd docker
sudo usermod -aG docker $USER

GCC-3.4

按理说Docker可以很方便地pull下来指定版本的GCC,但是当你执行

1
docker pull gcc:3.4

发现DockerHub里并没有对应的版本,经查发现仓库里最老的版本就是4.8了,再之前的就要另想办法。

于是我pull下了Ubuntu bionic,参照Mocc讨论区中老师给出的askubuntu社区链接中的方法进行操作,这上面有人提供了一个2007年版本的古老源,我使用入站不限速(约500Mbps)的阿里云服务器fetch速度只有2KB/s,看来想要下载完这700MB的包有点不可能,于是另寻他法。

最终方案

灵机一动,去实验楼的虚拟机上看看他是怎么个情况不就好了?于是有了下面的方案:

1
2
3
4
5
6
7
8
9
10
#获取Ubuntu precise镜像
docker pull ubuntu:precise

#查看镜像列表
docker image ls

#创建基于Ubuntu precise的容器实例,-it的意义是终端交互
docker run -it ubuntu:precise

#按Ctrl + D可以返回shell

前期准备

由于docker容器中没有文本编辑器,所以需要暂时退出docker做好准备。

1
2
3
#回到服务器bash,新建sources.list
cd ~
vim sources.list

第一步,输入以下内容并保存(已注释掉了源码源,可根据需要来定)

1
2
3
4
5
6
7
8
9
10
deb http://mirrors.cloud.aliyuncs.com/ubuntu/ precise main restricted universe multiverse
deb http://mirrors.cloud.aliyuncs.com/ubuntu/ precise-security main restricted universe multiverse
deb http://mirrors.cloud.aliyuncs.com/ubuntu/ precise-updates main restricted universe multiverse
deb http://mirrors.cloud.aliyuncs.com/ubuntu/ precise-proposed main restricted universe multiverse
deb http://mirrors.cloud.aliyuncs.com/ubuntu/ precise-backports main restricted universe multiverse
#deb-src http://mirrors.cloud.aliyuncs.com/ubuntu/ precise main restricted universe multiverse
#deb-src http://mirrors.cloud.aliyuncs.com/ubuntu/ precise-security main restricted universe multiverse
#deb-src http://mirrors.cloud.aliyuncs.com/ubuntu/ precise-updates main restricted universe multiverse
#deb-src http://mirrors.cloud.aliyuncs.com/ubuntu/ precise-proposed main restricted universe multiverse
#deb-src http://mirrors.cloud.aliyuncs.com/ubuntu/ precise-backports main restricted universe multiverse

这里使用的是阿里云的源,如果特别钟爱TUNA或者中科大或者想使用其他源需要注意docker容器里默认无法支持https的源,需要手动安装插件:

1
sudo apt-get install apt-transport-https

但是鉴于国外源的连接性,还是别折腾了

第二步,手动下载deb(共5个),并保存在新建目录下

1
2
3
4
5
6
7
8
9
10
#新建目录
mkdir gcc-3.4
cd gcc-3.4

#下载deb包,如果是32位系统就改成i386
wget http://old-releases.ubuntu.com/ubuntu/pool/universe/g/gcc-3.4/gcc-3.4-base_3.4.6-6ubuntu3_amd64.deb
wget http://old-releases.ubuntu.com/ubuntu/pool/universe/g/gcc-3.4/gcc-3.4_3.4.6-6ubuntu3_amd64.deb
wget http://old-releases.ubuntu.com/ubuntu/pool/universe/g/gcc-3.4/cpp-3.4_3.4.6-6ubuntu3_amd64.deb
wget http://old-releases.ubuntu.com/ubuntu/pool/universe/g/gcc-3.4/g++-3.4_3.4.6-6ubuntu3_amd64.deb
wget http://old-releases.ubuntu.com/ubuntu/pool/universe/g/gcc-3.4/libstdc++6-dev_3.4.6-6ubuntu3_amd64.deb

将准备的文件导入容器中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#获取容器编号,例如4ab62463177b
docker ps -a

#将sources.list和gcc-3.4导入容器root路径下
docker cp sources.list 4ab62463177b:/root/
docker cp gcc-3.4 4ab62463177b:/root/

#若容器未启动,则需要启动容器
docker start 4ab62463177b

#若容器已启动,有两种方式进入容器
#attach 这种方式退出会使容器结束运行
docker attach 4ab62463177b
#exec 运行程序,退出不会结束容器,推荐
docker exec -it 4ab62463177b /bin/bash

安装环境

进入docker容器后,现在开始安装环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#找到导入的准备文件
cd ~

#将sources.list覆盖到系统设置
mv sources.list /etc/apt/sources.list

#更新
apt-get update

#安装GCC-3.4
cd gcc-3.4/
dpkg -i *.deb

#这时候大概率失败,提示某些依赖未安装,尝试修复
apt-get install binutils libc6-dev
apt-get -f install

#再次安装
dpkg -i *.deb

#查看安装是否成功
gcc-3.4 -v

这时GCC-3.4便安装好了,再将其他依赖安装好就可以编译了

1
2
3
4
#常见make工具
apt-get install automake autoconf libtool make
#我一个一个根据错误提示试出来的
apt-get install bin86 gcc gcc-multilib

然后就可以编译啦

1
2
3
4
5
6
7
8
make

#查看生成的文件Image
cd ~/oslab/linux-0.11/

#可以选择保存docker镜像,先退出容器
docker commit -a '作者' -m '信息' 4ab62463177b Name:Tag
docker image ls

X11转发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#尝试进行下一步
cd ~/oslab/
./run

#然后出现各种问题
#如果提示No such file or directory,说明你的系统是64位的,需要安装32位库
apt-get install lib32ncurses5

#如果可以运行但报错,则需要安装依赖(一个一个手动翻源翻出来的)
apt-get install libsm6:i386 libx11-6:i386 libxpm4:i386 libstdc++6:i386

#这时候就可以运行了
./run

#然后你就会发现这玩意竟然需要GUI???是不是有种被骗了的感觉,我也是

没办法,把方案做完吧,学习的目的在于过程嘛,至于这个结果…我们可以曲线救国,用X11转发GUI程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#这是将docker容器中的GUI转发到宿主机的方法,二次转发到本地没有成功
#使用之前制作的镜像ubuntu:gcc-3.4新建容器,有两种方法
#TCP连接
docker run -it \
-v /etc/localtime:/etc/localtime:ro \
--net=host \
-e DISPLAY=:10.0 \
-v $HOME/slides:/root/slides \
-e GDK_SCALE \
-e GDK_DPI_SCALE \
ubuntu:gcc-3.4 \
/bin/bash

#UDS连接,大家都在夸UNIX SOCKET的好,可我一次也没成功过
docker run -it \
-v /etc/localtime:/etc/localtime:ro \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-e DISPLAY=unix$DISPLAY \
-e GDK_SCALE \
-e GDK_DPI_SCALE \
ubuntu:gcc-3.4

#或者不新建,使用以下命令改变$DISPLAY
export DISPLAY=宿主机IP

#一个检验是否可以X11转发的小工具
apt-get install xarclock
#如果配置成功会在本地弹出窗口显示(需要XManager或同类软件)
xarclock

如果是服务器没有GUI的话,我们将编译好的镜像拿出来:

1
2
3
4
5
6
7
8
9
10
11
docker cp 4ab62463177b:/root/oslab/linux-0.11/Image ~/oslab/linux-0.11/Image
#安装环境
sudo apt install lib32ncurses5 libsm6:i386 libx11-6:i386 libxpm4:i386 libstdc++6:i386
sudo apt-get install x11-xserver-utils

#打开权限
xhost +

#运行脚本
cd ~/oslab
./run

成功显示~

后记

这东西折腾了一天…觉得还是有必要记录下来的。

后来尝试用虚拟机,找个老版本Ubuntu,按照上面的流程,竟然十几分钟就搞好了… 感觉白折腾了这么一大圈,就权当练习了Docker的使用吧。