事前准备
一台Ubuntu工作机,最好是同样的Ubuntu20.04。因为编译的时候要用到开发机的配置文件。如果版本不一样的话,低编高根本执行不下去,高编低也会有警告。
工作机的语言设置成与基板一致(英语)
安装必要工具
其中debootstrap就是ubuntu提供的制作工具。另外两个是chroot的前提。
apt-get install qemu-user-static debootstrap binfmt-support
开发机(不包括chroot)的动作
整个制作过程的中间一部分是利用chroot命令进入下载目录,然后以登录要做成的根文件系统的方式修改的,所以以chroot作为分界。
可以把开发机上的动作存成outer.sh,把chroot内的动作存成inner.sh,便于以后操作。
本文中所有工具和编译链都是arm64,如果要编32位系统请自行查找替换,应该也没几处。
具体解释见注释吧
# focal是ubuntu20.04的代号。Ubunntu不同的版本有不同的代号。 export targetdir=ubuntu20.04-rootfs export distro=focal # 打包好的根文件系统直接放到tftp服务目录,便于更新,非必须。 export tftpdir=/var/tftp mkdir $PWD/$targetdir # debootstrap是Ubuntu创建文件系统的工具 # 架构根据实际基板和操作系统直接选。 sudo debootstrap --arch=arm64 --foreign $distro $PWD/$targetdir # qemu是整个步骤的一个灵魂,不拷贝进去chroot命令会失败 sudo cp /usr/bin/qemu-aarch64-static $PWD/$targetdir/usr/bin # 保证目录内的网络环境 sudo cp /etc/resolv.conf $PWD/$targetdir/etc # 如果把提到的chroot后的动作存成一个shell的话, sudo cp ./inner.sh $PWD/$targetdir/ # 不进行mount的话某些安装动作会失败 sudo mount -vt proc proc $PWD/$targetdir/proc sudo mount -vt devpts devpts -o gid=5,mode=620 $PWD/$targetdir/dev/pts sudo chroot $PWD/$targetdir # ====接下来的动作要在已经下载的文件系统目录中进行==== # 从chroot文件夹中exit之后回到这里 # 反挂载proc sudo umount $PWD/$targetdir/proc sudo umount $PWD/$targetdir/dev/pts # 删除chroot用的工具 sudo rm -f $PWD/$targetdir/usr/bin/qemu-aarch64-static sudo rm -f $PWD/$targetdir/work_in_ubuntu.sh # 打包并拷贝。非要进入文件夹内再打包是为了保证解包后的文件路径不出麻烦。 cd $PWD/$targetdir && sudo tar -zvcf rootfs-ubuntu.tar.gz . && sudo mv rootfs-ubuntu.tar.gz $tftpdir && cd -
其实在外层开发环境中能干的事情不多。
目标系统chroot的动作
可干的事情非常多。简单说就是在命令行下安装和配置Ubuntu的各种服务。
下面的内容可以存成一个inner.sh
当然也可以手动一行一行执行。
这里我遇到了三个难点。第一是语言包的问题,我一开始开发环境是中文,导致里面的语言怎么设都报警告,切换成英文万事大吉。第二是网络配置,Ubuntu20.04默认的netplan没配明白,安装了network-manager之后还是配了半天。第三是关于U盘自动挂载的,那个更麻烦,不过不影响rootfs本身的功能,以后有机会再写。
# 现在已经不是刚才的环境了,所以要重设一遍环境变量。 export distro=focal export LANG=C export hostname=myboard #debootstrap的位置不一样了。 /debootstrap/debootstrap --second-stage #指定ubuntu的源文件位置。可以替换成清华源、阿里云源之类。注意替换的时候网址里要用<strong>ubuntu-ports</strong>,否则ubuntu指向的都是x86_64的源,就白忙活了。 cat <<EOT > /etc/apt/sources.list deb http://ports.ubuntu.com/ubuntu-ports focal main restricted universe multiverse deb-src http://ports.ubuntu.com/ubuntu-ports focal main restricted universe multiverse deb http://ports.ubuntu.com/ubuntu-ports focal-updates main restricted universe multiverse deb-src http://ports.ubuntu.com/ubuntu-ports focal-updates main restricted universe multiverse deb http://ports.ubuntu.com/ubuntu-ports focal-security main restricted universe multiverse deb-src http://ports.ubuntu.com/ubuntu-ports focal-security main restricted universe multiverse EOT cat <<EOT > /etc/apt/apt.conf.d/71-no-recommends APT::Install-Recommends "0"; APT::Install-Suggests "0"; EOT # 更新已安装的服务 apt-get -y update apt-get -y upgrade # 安装各种工具。可以写在一起,但分开的好处是便于增删改。 # 第一波是(自认为)系统必须的 # 1.locale apt-get install -y locales dialog #设语言,语言要先改了,不然会影响一些工具的安装。 #这里会弹一个对话框,就正常选就行。 dpkg-reconfigure locales update-locale LANG="en_US.UTF-8" LANGUAGE="en_US:en" # 设时区 echo "Asia/Shanghai" > /etc/timezone dpkg-reconfigure -f noninteractive tzdata # 2.网络工具 # ifconfig默认不带,需要装。 apt-get install -y net-tools # Ubuntu默认的网络配置没配明白,于是需要借助network-manager工具 apt-get install -y network-manager # 3.进程工具,ps,kill什么的 apt-get install -y psmisc # 4.网络时间 apt-get install -y ntpdate ntp # 5.NTFS文件系统工具(主要是U盘) apt-get install -y fuse ntfs-3g # 6.busybox apt-get install -y busybox # 工具安装先放一放,开始搞配置 # hostname可以随便起,但不能没有 echo $hostname > /etc/hostname # fstab # 我的fstab下面这样,实际根据自己基板的情况自行替换 #fstab cat <<EOT> /etc/fstab /dev/root / auto defaults 1 1 /dev/mmcblk0p1 /boot auto defaults 0 0 proc /proc proc defaults 0 0 tmpfs /var/volatile tmpfs defaults 0 0 EOT # #非常重要# # 这个文件是告诉Ubuntu有哪些可以用的串口。不写这个文件会出现加载到文件系统后串口就失去回显了 cat <<EOT > /etc/securetty ttyPS0 ttyPS1 ttyS0 ttyS1 ttyS2 ttyS3 ttyUSB0 ttyUSB1 ttyUSB2 ttyGS0 EOT # 配置网络环境,IP地址通过DHCP获取。 cat << EOT > /etc/netplan/eth0.yaml network: version: 2 renderer: networkd ethernets: enp3s0: dhcp4: y dhcp6: n EOT # #非常重要# # 刚才安装的network-manager的默认配置,不写的话系统启动之后网卡是down的 cat <<EOT >/etc/NetworkManager/conf.d/10-globally-managed-devices.conf [keyfile] unmanaged-devices=none EOT # 时间同步脚本 cat << EOT > /usr/lib/networkd-dispatcher/routable.d/ntp-restart #!/bin/sh /usr/sbin/service ntp restart EOT # 配置用户 # 修改root用户密码,可以自行发挥。这种写法为了避免出现交互界面,少打字 echo root:root | chpasswd # 追加一个ubuntu用户 useradd -p $(openssl passwd -crypt ubuntu) ubuntu echo "ubuntu ALL=(ALL:ALL) ALL" > /etc/sudoers.d/ubuntu # 安装其它需要的工具,不一一解释了,每个项目需求都不一样,酌情增减。 apt-get install -y build-essential apt-get install -y kmod apt-get install -y iperf apt-get install -y curl apt-get install -y usbutils apt-get install -y hwinfo apt-get install -y openssh-server # 清除安装产生的临时文件 apt-get clean #自动退出,exit后就回到外层了 exit
再次修改文件系统
弄懂上面两步的话就很简单。如果是单纯的拷贝往文件夹里拷就行。如果要增删改服务,就是外层的debootstrap不要做,逐行执行命令直到chroot,然后修改配置或者apt安装反安装。注意反安装的话remove之后最好执行一遍purge,删除未使用的配置文件。不要乱改语言。退出前记得clean。
你这遇到的三个大坑,我:
1. 不论啥环境,Linux下清一色 en-US.UTF8,然后手动安装中文字体(如果默认没有的话)。其他国家我不知道,中文区基本上开源社区也是不会向上做贡献的,导致中文语言环境基本上处于能不用就不用的状态。
2. 早年(2011)那阵公司就解决不了CentOS567的网络配置,到后面(2018)公司照旧解决不了Ubuntu16的网络配置。基本上前者就是干脆写死个if-cfg,然后等着安装完成后默认第一个网卡有个固定IP;后者基本一样,区别在于chroot后使用nmcli,因为if-cfg不靠谱了。
3. 如果我没记错的话,早些时候自动挂载是实现不了的,网上的教程基本上只能挂载已知设备,通过设备名或者设备UUID的,基本是笑话。现在的话,大概需要写一堆udev脚本,能实现,就是很麻烦,收益也很小。
另外这种线下产品,大规模使用apt不好吧,可能会导致不同时间出来的结果区别特别大。我们当时全部都是基于本地的光盘镜像实现的,最小化安装,然后第一次启动后再执行一个安装脚本安装额外的RPM/DEB包。不过那时全是X86-64架构,对于ARM我不清楚。
第一个问题是我一开始小看了语言这东西了,没想到会出那么多警告。
第二个,固定IP很容易搞定,但我们领导见了鬼的要求DHCP。
第三个,确实是通过udev规则搞定的。
我们这个客户是个纯硬件客户,软件几乎一窍不通。你要说它啥都不懂吧,还一上来就指定要Ubuntu。你说你要什么软件服务我给你配就完了,为啥要指定Ubuntu呢?现在这个阶段只需要确定能不能用即可,到下一步真正跑起来就不能用apt了。
语言这东西,当年英国佬整树莓派默认en.UK-UTF8那大坑差点没摔死我……
DHCP基本不自己动手就搞不定,Linux社区还想把网络控制也搞进systemctl呢,基本上是摔死在里面了。
上来就要求Ubuntu估计就是被民间忽悠瘸了,以为CentOS被卖了Debian等死了。不过说到底企业级的生产设备我真就只会选Ubuntu/CentOS/Debian这仨玩意,熟啊方案多啊靠谱啊。Arch基本上只见过业余选手当玩具然后吹牛逼,企业级生产环境上用Arch的没见过活人。更猛的大神直接自己全部从源码编译,内核都自己搞,不管什么发行版,然后到开发环节就蔫了,根本带不动团队,还是发行版香。
徒手写系统吗?
怎么可能。东西都在服务器上,徒手攒罢了。