Hi3798MV200 恩兔N2 NS-1 (三): 製作 Ubuntu rootfs

来源:https://www.cnblogs.com/milton/archive/2023/08/07/17599562.html
-Advertisement-
Play Games

如果把整個Linux操作系統看作層級關係, 根文件系統是位於內核之上的模塊,對於同樣的硬體和架構, Linux各個發行版的區別主要在於根文件系統, 而底層的內核部分幾乎是一樣的. 通過製作根文件系統, 可以更換成其它發行版, 定製自己的最小化安裝. ...


目錄

關於根文件系統 rootfs

在 Linux 中, 所有的文件和目錄被組織成一個樹狀的結構, 而根文件系統, rootfs, the root filesystem, 位於文件樹的頂層(路徑'/'). Linux 內核通過 root = 設置的參數掛載 rootfs. 在根文件系統中也包含了其它文件樹的掛載點(mount points), 用於將其它文件(設備)掛載到當前環境中, 形成完整的系統.

在根文件系統中包含了用於系統啟動和操作的關鍵文件. 系統引導啟動程式會在根文件系統掛載之後執行初始化腳本(如rcS, init.d, profile).

如果把整個Linux操作系統看作層級關係, 根文件系統是位於內核之上的模塊,對於同樣的硬體和架構, Linux各個發行版的區別主要在於根文件系統, 而底層的內核部分幾乎是一樣的. 通過製作根文件系統, 可以更換成其它發行版, 定製自己的最小化安裝.

文件準備

底包

本例使用的是稍息版 Debian 10, 替換成 Ubuntu20.04.

從 stretch.tar.bz2 中提取驅動部分, 位於 /lib/modules/4.4.35-hi3798mv2x/

下載 ubuntu-base

從國內鏡像站點, 下載 ubuntu-base 包

解壓

在本地創建工作目錄, 將壓縮包解壓到工作目錄下, 註意要用 sudo + -p(-p, --preserve-permissions)參數, 保留原owner和原許可權

mkdir workroot
sudo tar -xpf ubuntu-base-20.04.5-base-arm64.tar.gz -C workroot/

初始的目錄大小為77MB左右. 可以檢查一下 workroot 下的文件目錄, owner是否為 root.

關於為什麼要用 sudo

Even if you use tar's --same-owner flag, you will still need to extract the files as root to preserve ownership.
--same-owner flag is on by default for root.
--no-same-owner, extract files as yourself, which is default for ordinary users

準備 resolv.conf

base系統中 resolv.conf 為空, 需要設置 nameserver 否則 chroot 後目標系統 apt install 時無法解析功能變數名稱

選項一, 複製

複製 resolv.conf 到目標系統

sudo cp /etc/resolv.conf workroot/etc/resolv.conf

選項二, 直接寫

echo "nameserver 127.0.0.53" | sudo tee workroot/etc/resolv.conf

複製 qemu-xxx-static

安裝 qemu-user-static, 這個包裡面有各個架構的二進位執行文件, 會安裝到 /usr/bin

sudo apt install qemu-user-static

對於 armhf, 複製 qemu-arm-static; 對於 arm64 複製 qemu-aarch64-static

# armhf
sudo cp /usr/bin/qemu-arm-static workroot/usr/bin/
# arm64
sudo cp /usr/bin/qemu-aarch64-static workroot/usr/bin/

在進行下一步之前檢查文件格式是否正確, 32位的 armhf 用 qemu-arm-static, 64位的 arm64 用 qemu-aarch64-static

# armhf
sudo chroot workroot/ /usr/bin/qemu-arm-static /bin/ls
# arm64
sudo chroot workroot/ /usr/bin/qemu-aarch64-static /bin/ls

如果文件架構不匹配, 會提示- /bin/ls: Invalid ELF image for this architecture

修改目標系統軟體源

vi workroot/etc/apt/sources.list

替換為USTC源

: %s/http:\/\/ports.ubuntu.com\/ubuntu-ports\//http:\/\/mirrors.ustc.edu.cn\/ubuntu-ports\//gc

掛載目標系統

選項一: 手工掛載

掛載目錄

sudo mount -t proc /proc    workroot/proc
sudo mount -t sysfs /sys    workroot/sys
sudo mount -o bind /dev     workroot/dev
sudo mount -o bind /dev/pts workroot/dev/pts

切換根目錄

sudo chroot workroot/

如果前面的檢查沒問題, 但是這一步總是提示 '/bin/bash': Exec format error, 檢查一下 binfmts 是否開啟

update-binfmts --display

正常應該顯示如下, 對應格式為 enabled,

qemu-aarch64 (enabled):
     package = qemu-user-static
...
qemu-arm (enabled):
     package = qemu-user-static
...

如果顯示為 disabled, 需要檢查是否有軟體未安裝. 安裝了 Docker 的 Ubuntu 環境可能會有衝突.

$ mount | grep binfmt
systemd-1 on /proc/sys/fs/binfmt_misc type autofs (rw,relatime,fd=29,pgrp=1,timeout=0,minproto=5,maxproto=5,direct,pipe_ino=18150)
binfmt_misc on /proc/sys/fs/binfmt_misc type binfmt_misc (rw,nosuid,nodev,noexec,relatime)

選項二: 使用腳本掛載

以上的操作, 可以通過一個腳本進行簡化

#!/bin/bash
mnt() {
    echo "MOUNTING"
    sudo mount -t proc /proc    ${2}proc
    sudo mount -t sysfs /sys    ${2}sys
    sudo mount -o bind /dev     ${2}dev
    sudo mount -o bind /dev/pts ${2}dev/pts
    sudo chroot ${2}
}
umnt() {
    echo "UNMOUNTING"
    sudo umount ${2}proc
    sudo umount ${2}sys
    sudo umount ${2}dev/pts
    sudo umount ${2}dev
}

if [ "$1" == "-m" ] && [ -n "$2" ] ;
then
    mnt $1 $2
elif [ "$1" == "-u" ] && [ -n "$2" ];
then
    umnt $1 $2
else
    echo ""
    echo "Either 1'st, 2'nd or both parameters were missing"
    echo ""
    echo "1'st parameter can be one of these: -m(mount) OR -u(umount)"
    echo "2'nd parameter is the full path of rootfs directory(with trailing '/')"
    echo ""
    echo "For example: ch-mount -m /media/sdcard/"
    echo ""
    echo 1st parameter : ${1}
    echo 2nd parameter : ${2}
fi

需要使用目標系統環境時

./mount.sh -m workroot/

定製 rootfs 內容

root@Box:/# uname -a
Linux Box 5.15.0-52-generic #58~20.04.1-Ubuntu SMP Thu Oct 13 13:09:46 UTC 2022 aarch64 aarch64 aarch64 GNU/Linux
# 檢查 mount
root@Box:/# mount
/proc on /proc type proc (rw,relatime)
/sys on /sys type sysfs (rw,relatime)
udev on /dev type devtmpfs (rw,nosuid,noexec,relatime,size=6965676k,nr_inodes=1741419,mode=755,inode64)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000)

添加驅動文件

僅使用kernel自帶的驅動可以啟動rootfs, 但是一些板載的外設, 例如SATA硬碟和USB, 會因為沒有驅動而無法識別. 需要手動將這些驅動放到rootfs中.

通過uname -r可以看到目標系統的架構為4.4.35-hi3798mv2x, 由此可以確定驅動的路徑為

/lib/modules/4.4.35-hi3798mv2x/

從前面準備的底包中, 將驅動部分文件提取後放到這個目錄下, 結構類似於

modules
└── 4.4.35-hi3798mv2x
    ├── kernel
    │   ├── crypto
    │   ├── drivers
    │   ├── fs
    │   ├── lib
    │   └── net
    ├── modules.alias
    ├── modules.alias.bin
    ├── modules.builtin
    ├── modules.builtin.alias.bin
    ├── modules.builtin.bin
    ├── modules.dep
    ├── modules.dep.bin
    ├── modules.devname
    ├── modules.order
    ├── modules.softdep
    ├── modules.symbols
    └── modules.symbols.bin

安裝基礎軟體

# 77M -> 300M
apt update
# 300M -> 304M
apt install nano sudo vim-tiny

修改軟體源vi /etc/apt/sources.list, 替換為USTC源

: %s/http:\/\/ports.ubuntu.com\/ubuntu-ports\//http:\/\/mirrors.ustc.edu.cn\/ubuntu-ports\//gc

再安裝其它軟體就快多了

apt upgrade
# 304M -> 440M
apt install openssh-server
# 440M -> 445M
apt install u-boot-tools net-tools sysstat smartmontools network-manager

安裝的軟體包中

  • openssh-server 提供 ssh 服務
  • u-boot-tools 提供 fw_printenv 和 fw_setenv 方法, 用於修改 UBOOT 啟動參數
  • net-tools 提供 ifconfig, netstat 等常用工具
  • sysstat 提供 iostat 等常用工具

基礎設置

設置網路

mkdir /etc/network/interfaces.d
echo auto eth0 > etc/network/interfaces.d/eth0
echo iface eth0 inet dhcp >> etc/network/interfaces.d/eth0

給 root 用戶設置密碼 註意 這一步別忘了

passwd

開啟 root 用戶 ssh 訪問, 編輯 /etc/ssh/sshd_config, 找到

#PermitRootLogin prohibit-password

替換為

PermitRootLogin yes

配置登錄的串口, 修改文件 /etc/systemd/system/getty.target.wants/[email protected]

vi /etc/systemd/system/getty.target.wants/getty\@tty1.service

ConditionPathExists=/dev/tty0

修改為實際的名稱

ConditionPathExists=/dev/ttyAMA0

清理文件

安裝完成後, 清理apt

apt autoremove
apt-get autoclean
apt-get clean
apt clean
# 結束後 368M

取消掛載目標系統

在目標系統上, exit 退出

結束後, 要先取消掛載

選項一: 手工取消掛載

sudo umount workroot/proc
sudo umount workroot/sys
sudo umount workroot/dev/pts
sudo umount workroot/dev

選項二: 通過腳本取消掛載

如果通過腳本, 則是

./mount.sh -u workroot/

製作 rootfs 鏡像文件

# 生成一個適當大小的空鏡像,這個大小參考du -h workroot
dd if=/dev/zero of=rootfs.img bs=1M count=1024
# 格式化 
mkfs.ext4 rootfs.img
# or
mkfs -t ext4 rootfs.img
# 掛載空鏡像
mkdir rootfs
sudo mount rootfs.img rootfs/
# 寫入文件, 保留許可權
sudo cp -rfp workroot/* rootfs/
# 取消掛載
sudo umount rootfs/
# 檢查文件系統並自動修複
e2fsck -p -f rootfs.img
# 使鏡像緊湊
resize2fs -M rootfs.img

問題和解決

Root 能串口登錄, 無法 ssh 登錄

這是因為 ssh 預設禁止 root 登錄, 編輯 /etc/ssh/sshd_config, 找到

#PermitRootLogin prohibit-password

替換為

PermitRootLogin yes

然後systemctl restart sshd重啟 sshd 服務

分區可用空間為0

這是因為鏡像壓縮後寫入, 分區大小就是鏡像大小, 需要通過 resize2fs /dev/[partition] 擴充分區

方案一: 使用腳本, 手工執行

創建 /usr/bin/local_resize.sh, 內容如下, chmod +x 設為可執行

#!/bin/bash
rootfs_partition=/dev/$(lsblk -l|grep /|awk '{print $1}')
logger -t "resize-disk[$$]" "resizing $rootfs_partition"
if [ "$(echo $rootfs_partition | grep "mmc")" = "" ];then
    rootfs_disk=$(echo "$rootfs_partition" |sed -E -e 's/^(.*)[0-9]+/\1/g')
else
    rootfs_disk=$(echo "$rootfs_partition" |sed -E -e 's/^(.*)p[0-9]+/\1/g')
fi

if [ "$rootfs_disk" = "/dev/mmcblk0" ]; then
    resize2fs $rootfs_partition 2>&1 > /dev/null
else
    rootfs_partition_num=$(echo "$rootfs_partition" |sed -E -e 's/^.*([0-9]+)/\1/g')
    startfrom=$(fdisk -l ${rootfs_disk} -o device,start|grep ${rootfs_partition}|awk '{print $2}')
    (echo d; echo $rootfs_partition_num; echo n; echo p; echo $rootfs_partition_num; echo $startfrom; echo ; echo p; echo w;) | fdisk $rootfs_disk
    sync
    resize2fs $rootfs_partition
fi
logger -t "resize-disk[$$]" "resized $rootfs_partition"

方案二: 使用 systemd service 在第一次啟動時執行

增加 /usr/sbin/local-resize2fs.sh , chmod +x 設為可執行

#!/bin/bash
if [ ! -f /etc/first_init ]; then
    rootfs_partition=/dev/$(lsblk -l|grep /|awk '{print $1}')
    logger -t "resize-disk[$$]" "resizing $rootfs_partition"
    if [ "$(echo $rootfs_partition | grep "mmc")" = "" ];then
        rootfs_disk=$(echo "$rootfs_partition" |sed -E -e 's/^(.*)[0-9]+/\1/g')
    else
        rootfs_disk=$(echo "$rootfs_partition" |sed -E -e 's/^(.*)p[0-9]+/\1/g')
    fi

    if [ "$rootfs_disk" = "/dev/mmcblk0" ]; then
        resize2fs $rootfs_partition 2>&1 > /dev/null
    else
        rootfs_partition_num=$(echo "$rootfs_partition" |sed -E -e 's/^.*([0-9]+)/\1/g')
        startfrom=$(fdisk -l ${rootfs_disk} -o device,start|grep ${rootfs_partition}|awk '{print $2}')
        #lastsector=$(fdisk -l ${rootfs_disk} -o device,end|grep ${rootfs_partition}|awk '{print $2}')
        (echo d; echo $rootfs_partition_num; echo n; echo p; echo $rootfs_partition_num; echo $startfrom; echo ; echo p; echo w;) | fdisk $rootfs_disk
        sync
        resize2fs $rootfs_partition
    fi
    echo `date +%s%N` > /etc/first_init
    logger -t "resize-disk[$$]" "resized $rootfs_partition"
fi
exit 0

增加service: /etc/systemd/system/resize2fs.service

[Unit]
Description=resize2fs local filesystem
Before=local-fs-pre.target
DefaultDependencies=no

[Service]
Type=oneshot
TimeoutSec=infinity
ExecStart=/usr/sbin/local-resize2fs.sh
RemainAfterExit=true

[Install]
RequiredBy=local-fs-pre.target

在 /etc/systemd/system/local-fs-pre.target.wants/ 下麵增加 resize2fs.service 的軟鏈, 使其生效

參考


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ###JDBC使用步驟總結 **1. 註冊驅動** ``` Class.forName("com.mysql.cj.jdbc.Driver"); ``` **2. 獲取鏈接** ``` Connection connection = DriverManager.getConnection(url,u ...
  • # 使用Python爬取公眾號的合集 # 前言 。。。最近老是更新關於博客的文章,很久沒更新其他的了,然後寫一下如何爬取微信公眾號裡面的圖片吧! 先看看微信公眾號的樣子吧: ![image](https://img2023.cnblogs.com/blog/3091176/202308/309117 ...
  • 一個 Go 文件包含以下幾個部分: - 包聲明 - 導入包 - 函數 - 語句和表達式 看下麵的代碼,更好地理解它: ## 例子 ```Go package main import "fmt" func main() { fmt.Println("Hello World!") } ``` ## 例子 ...
  • ### 歡迎訪問我的GitHub > 這裡分類和彙總了欣宸的全部原創(含配套源碼):[https://github.com/zq2599/blog_demos](https://github.com/zq2599/blog_demos) ### 本篇概覽 - 欣宸的月俸雖然很低,但還是咬著牙攢夠銀子 ...
  • ## 抽象靜態方法 在C# 11中,引入了對抽象靜態介面成員的支持。這個特性可以讓你在介面中定義靜態抽象方法、屬性、或事件。具體來說,一個介面可以定義一個或多個抽象靜態成員,這些成員沒有具體的實現。任何實現該介面的類或結構必須提供這些成員的靜態實現。這就像實現普通介面成員一樣,只不過是靜態的。當然, ...
  • SqlSugar ORM SqlSugar 是一款 老牌 .NET 開源多庫架構ORM框架 ,一套代碼能支持多種資料庫像Admin.net、Blog.Core、CoreShop等知名開源項目都採用了SqlSugar作為底層 特色1:超級簡單 在不用任何設計模式,任何框架的情況下都可以擁有最佳體驗,S ...
  • Debian 沒有像 Ubuntu 提供 Ubuntu-Base 那樣提供打包好的 rootfs, 但是 Debian 提供了一個製作 rootfs 的工具 debootstrap. 以下介紹使用 debootstrap 製作 Debian rootfs 的過程. ...
  • 1.常用命令 pwd 當前路徑 passwd 修改密碼 passwd root 修改root用戶密碼 date 顯示系統日期 sudo + 命令 調用管理許可權 clear 清屏 firefox 瀏覽器打開 shutdown -h now 關閉系統(1) init 0 關閉系統(2) telinit ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...