━━━━━━━━━━━━━━━━━━
                            RUST模块开发初探

                              Chateau Xiao
                  ━━━━━━━━━━━━━━━━━━


Table of Contents
─────────────────

1. 前提条件
2. 升级debian系统到 bookworm
3. 安装rust环境
.. 1. 初始化rustup环境
.. 2. 安装 1.62.0 版本
.. 3. 指定 cargo 源 (可选)
.. 4. 指定bindgen版本
.. 5. 安装 rust-src 标准库源代码
4. 安装 llvm/clang 工具链
5. 内核编译与安装
.. 1. .config 编译选项
..... 1. rust支持选项详解
..... 2. 检查rust 是否可以在内核可用
.. 2. 方案1(可选) kernel.org 官方内核源码下载
..... 1. 下载方法
..... 2. 内核配置
..... 3. 源代码编译
..... 4. 安装
.. 3. 方案2(推荐) debian.org 发行版内核源码下载
..... 1. 下载
..... 2. 内核配置
..... 3. deb安装包构建
..... 4. 安装
.. 4. 备份 .config 文件并重启系统
6. Rust 的 hello world 模块
.. 1. 内核自带模块的加载
.. 2. out-of-tree rust模块加载
..... 1. 检查 kernel_src 目录的 .o 文件是否存在
..... 2. 拉取 out-of-tree 代码
..... 3. 加载测试





1 前提条件
══════════

  • 硬盘空间 50GB
  • x86 cpu(目前rust尚不支持包括arms在内的其他架构的cpu)
  • debian linux (升级到当前最新版本bookworm)


2 升级debian系统到 bookworm
═══════════════════════════

  • 支持 linux 6.0.0-6 内核

  ┌────
  │ sed -i 's/deb.debian.org/mirrors.163.com/g' /etc/apt/sources.list
  │ sed -i 's/http:/https:/g' /etc/apt/sources.list
  │ apt-get update -y
  │ apt-get upgrade -y
  │
  │ # install the latefst distribution version of kernel
  │ apt-get install  -y linux-image-amd64  # 4.19.0
  │
  │ # upgrade to bullseye
  │ sed -i 's/buster/bullseye/g' /etc/apt/soufrces.list
  │ apt-get update -y
  │ apt-get dist-upgrade -y
  │ apt-get autoremove -y
  │ apt-get install  -y linux-image-amd64  # 5.10.0
  │ reboot
  │ apt-get upgrade -y
  │
  │ # upgrade to bookworm
  │ sed -i 's/bullseye/bookworm/g' /etc/apt/sources.list
  │ apt-get update -y
  │ apt-get dist-upgrade -y
  │ apt-get autoremove -y
  │ apt-get install  -y linux-image-amd64 # 6.0.0-6
  │ reboot
  │ apt-get upgrade -y
  └────


3 安装rust环境
══════════════

  参考官方文档执行如下步骤[1]
  <https://www.kernel.org/doc/html/latest/rust/quick-start.html>


3.1 初始化rustup环境
──────────

  ┌────
  │ curl https://sh.rustup.rs -sSf | sh
  └────

        :~# curl <https://sh.rustup.rs> -sSf | sh
        info: downloading installer

        Welcome to Rust!

        This will download and install the official compiler for the Rust
        programming language, and its package manager, Cargo.

        Rustup metadata and toolchains will be installed into the Rustup
        home directory, located at:

          /root/.rustup

        This can be modified with the RUSTUP_HOME environment variable.

        The Cargo home directory is located at:

          /root/.cargo

        This can be modified with the CARGO_HOME environment variable.

        The cargo, rustc, rustup and other commands will be added to
        Cargo's bin directory, located at:

          /root/.cargo/bin

        This path will then be added to your PATH environment variable by
        modifying the profile files located at:

          /root/.profile
          /root/.bashrc

        You can uninstall at any time with rustup self uninstall and
        these changes will be reverted.

        Current installation options:


           default host triple: x86_64-unknown-linux-gnu
             default toolchain: stable (default)
                       profile: default
          modify PATH variable: yes

        *1) Proceed with installation (default)*
        2) Customize installation
        3) Cancel installation
        >1

        info: profile set to 'default'
        info: default host triple is x86_64-unknown-linux-gnu
        info: syncing channel updates for 'stable-x86_64-unknown-linux-gnu'
        info: latest update on 2022-12-15, rust version 1.66.0 (69f9c33d7
2022-12-12)
        info: downloading component 'cargo'
          6.5 MiB /   6.5 MiB (100 %)   3.4 MiB/s in  1s ETA:  0s
        info: downloading component 'clippy'
        info: downloading component 'rust-docs'
         19.0 MiB /  19.0 MiB (100 %)   3.4 MiB/s in  5s ETA:  0s
        info: downloading component 'rust-std'
         29.7 MiB /  29.7 MiB (100 %)   3.2 MiB/s in  9s ETA:  0s
        info: downloading component 'rustc'
         68.0 MiB /  68.0 MiB (100 %)   3.2 MiB/s in 21s ETA:  0s
        info: downloading component 'rustfmt'
          4.4 MiB /   4.4 MiB (100 %)   2.6 MiB/s in  1s ETA:  0s
        info: installing component 'cargo'
        info: installing component 'clippy'
        info: installing component 'rust-docs'
         19.0 MiB /  19.0 MiB (100 %)   2.5 MiB/s in  8s ETA:  0s
        info: installing component 'rust-std'
         29.7 MiB /  29.7 MiB (100 %)  13.9 MiB/s in  2s ETA:  0s
        info: installing component 'rustc'
         56.0 MiB /  68.0 MiB ( 82 %)  13.8 MiB/s in  4s ETA:  0sConnection
to 127.0.0.1 closed by remote host.
        Connection to 127.0.0.1 closed.


3.2 安装 1.62.0 版本
──────────

  ┌────
  │ rustup update 1.62.0
  │ rustup default 1.62.0
  │ rustc --version
  └────

  • 执行完成后检查版本
        # rustc –version
        rustc 1.62.0 (a8314ef7d 2022-06-27)


3.3 指定 cargo 源 (可选)
─────────────

  编辑文件 ~/.cargo/config
  ┌────
  │ [source.crates-io]
  │ registry = "https://github.com/rust-lang/crates.io-index";
  │ replace-with = 'sjtu'
  │
  │ [source.sjtu]
  │ registry = "https://mirrors.sjtug.sjtu.edu.cn/git/crates.io-index/";
  └────

  需要其他国内源的朋友可以参考如下文档[2]。


3.4 指定bindgen版本
─────────

  ┌────
  │ cargo install --vers 0.56.0 bindgen
  └────


3.5 安装 rust-src 标准库源代码
───────────────

  ┌────
  │ rustup component add rust-src
  │ rustup component add rustfmt
  │ rustup component add clippy
  └────


4 安装 llvm/clang 工具链
════════════════════════

  • 文档[3] <https://apt.llvm.org/>

  ┌────
  │ apt install lsb-release wget software-properties-common gnupg
  │ bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)"
  │ apt-get install clang-format clang-tidy clang-tools clang clangd
libc++-dev libc++1 libc++abi-dev libc++abi1 libclang-dev libclang1
liblldb-dev libllvm-ocaml-dev libomp-dev libomp5 lld lldb llvm-dev
llvm-runtime llvm python3-clang
  └────


5 内核编译与安装
════════════════

  执行下面命令,安装内核编译必须的系统工具:
  ┌────
  │ apt-get install -y git build-essential libncurses5-dev fakeroot wget
qtbase5-dev bison flex libelf-dev libssl-dev kmod cpio bc rsync zstd
  └────


5.1 .config 编译选项
──────────

  来到内核源代码目录下执行如下命令,先用图形化界面选定一些我们需要日常使
  用的参数,选择完毕后进行save保存. 默认保存为 .config 文件.
  ┌────
  │ make LLVM=1 menuconfig
  └────


5.1.1 rust支持选项详解
╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌

  要开启rust 支持,先要打开下表中对应的menuconfig选项并选中。

  ┌────
  │ Kernel hacking
  │     -> Sample kernel code
  │         -> Rust samples
  │            -> <M>   Minimal
  │            -> [*]   Host programs
  │     -> Rust hacking
  │         -> [*] Debug assertions
  │         -> [*] Overflow checks
  └────

  但我打开后并没有显示下面的子选项,rust相关的部分均为不可选的状态。后查
  询据官方文档[1]得知,需要先打开和 CONFIG_RUST 的编译开关的选项,才能正
  常显示上述选项。

  后来经过一番查找查找[4],才发现 CONFIG_RUST 并非一个单独存在的选项,而
  是需要有一系列与之有互斥关系的开关一起配置,该选项才会出现。

  执行如下命令可以查询相关的几个互斥选项是否已经正确:
  ┌────
  │ egrep
'CONFIG_GCC_PLUGINS|CONFIG_GCC_PLUGINS|CONFIG_MODVERSIONS|_RUST|RANDSTRUCT|CONFIG_DEBUG_INFO_BTF|CONFIG_RUST_BUILD_ASSERT_ALLOW'
.config
  └────

  显示返回, 注意下述返回结果当中,选项为 =y 的选项必须全部出现在
  .config 文件中,并且同时 被注释的 `is not set` 的变量的状态,也必须和
  下面配置文件的结果保持一致。

  ┌────
  │ CONFIG_RUST_IS_AVAILABLE=y
  │ CONFIG_RUST=y
  │ CONFIG_RUSTC_VERSION_TEXT="rustc 1.62.0 (a8314ef7d 2022-06-27)"
  │ CONFIG_HAVE_RUST=y
  │ # CONFIG_MODVERSIONS is not set
  │ CONFIG_RANDSTRUCT_NONE=y
  │ # CONFIG_DEBUG_INFO_BTF is not set
  │ CONFIG_RUST_DEBUG_ASSERTIONS=y
  │ CONFIG_RUST_OVERFLOW_CHECKS=y
  │ CONFIG_RUST_BUILD_ASSERT_ALLOW=y
  └────


5.1.2 检查rust 是否可以在内核可用
╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌

  ┌────
  │ make LLVM=1 rustavailable
  └────

  如果上述的安装都已经正确,则执行完成后会出现如下结果。
        *Rust is available!*

  未出现上述提醒,则编译环境将不会出现rust选项,请按照前面 `安装rust环境
  ` 的步骤重新配置。


5.2 方案1(可选) kernel.org 官方内核源码下载
──────────────────────

5.2.1 下载方法
╌╌╌╌╌╌╌╌╌╌╌╌╌╌

  ┌────
  │ git clone
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git -b
master kernel_src
  │ cd kernel_src
  │ git checkout v6.1
  └────

  _注意_: 由于源代码比较大,网速不好的情况下,执行上面命令的时间会需要比
  较久。


5.2.2 内核配置
╌╌╌╌╌╌╌╌╌╌╌╌╌╌

  参考上一节 _.config 编译选项_ ,这里不再赘述。


5.2.3 源代码编译
╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌

  ┌────
  │ cd kernel_src
  │ make LLVM=1 -j 12
  └────

  _注意_:上述命令要使用 LLVM 进行编译,但是编译速度会比默认的gcc编译器慢。


5.2.4 安装
╌╌╌╌╌╌╌╌╌╌

◊ 5.2.4.1 安装内核与模块

  ┌────
  │ make LLVM=1 modules_install
  │ make LLVM=1 install
  └────


◊ 5.2.4.2 调整引导程序

  ┌────
  │ update-initramfs -u
  │ update-grub2
  └────


5.3 方案2(推荐) debian.org 发行版内核源码下载
───────────────────────

5.3.1 下载
╌╌╌╌╌╌╌╌╌╌

  增加如下几项到配置文件 /etc/apt/sources.list ,该部分我选择了国内较快
  的163源进行配置,你也可以根据官方提供的地址[5]自行配置其他的源。
        deb-src https://mirrors.163.com/debian/bookworm main non-free
contrib
        deb-src https://mirrors.163.com/debian-security/bookworm-security
main
        deb-src https://mirrors.163.com/debian/bookworm-updates main
non-free contrib
        deb-src https://mirrors.163.com/debian/bookworm-backports main
non-free contrib

  然后执行如下命令下载6.1版本的debian发行版代码(带补丁修复)到指定目录
  ┌────
  │ apt-get update -y
  │ apt-get install linux-source-6.1
  │ cd /usr/src/
  │ tar -xvJf linux-source-6.1.tar.xz
  └────


5.3.2 内核配置
╌╌╌╌╌╌╌╌╌╌╌╌╌╌

  参考第一节 _.config 编译选项_ ,这里不再赘述。


5.3.3 deb安装包构建
╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌

  ┌────
  │ cd /usr/src/linux-source-6.1
  │ make LLVM=1 -j 12 bindeb-pkg
  └────

  执行完成后检查编译完成的结果

        ls -lh ../linux-*
          linux-headers-6.1.8_6.1.8-1_amd64.deb
          linux-image-6.1.8_6.1.8-1_amd64.deb
          linux-image-6.1.8-dbg_6.1.8-1_amd64.deb
          linux-libc-dev_6.1.8-1_amd64.deb
          linux-upstream_6.1.8-1_amd64.buildinfo
          linux-upstream_6.1.8-1_amd64.changes


5.3.4 安装
╌╌╌╌╌╌╌╌╌╌

  完成编译构建成deb包后,会看到上一层目录下生成了几个 deb 包。

  • 查看构建结果如下:

        md5sum ../linux-*
        edd421c4793893f8cb617352ca8326ed
 linux-headers-6.1.8_6.1.8-1_amd64.deb
        cd2494c870b48f1664815a9950058d25
 linux-image-6.1.8_6.1.8-1_amd64.deb
        a43160d12fce9d350014f9f88b49ef78
 linux-image-6.1.8-dbg_6.1.8-1_amd64.deb
        7e349e33f9d5a8305e5e50f34497c9cf  linux-libc-dev_6.1.8-1_amd64.deb
        7ff55025070c7e461c6653f5c57a4f27
 linux-upstream_6.1.8-1_amd64.buildinfo
        da5675ca7697d20d3263a562f69fc6a1
 linux-upstream_6.1.8-1_amd64.changes

  然后执行如下命令进行安装
  ┌────
  │ cd ../
  │ dpkg -i linux-*.deb
  └────


5.4 备份 .config 文件并重启系统
───────────────

  ┌────
  │ cp .config ~/rust-v6.1.config.v3  # 保留下次再用
  │ reboot
  └────

  _注意_: 安装完成后需要执行上述reboot命令重启一次机器,并且选择最新安装
  的内核作为引导,然后进行后面的步骤。


6 Rust 的 hello world 模块
══════════════════════════

6.1 内核自带模块的加载
───────────

  ┌────
  │ cd /usr/lib/modules/6.1.8/kernel/samples/rust/
  │ insmod rust_minimal.ko
  └────

  执行完成后执行dmesg查看加载是否成功,出现如下两行信息表示该模块顺利初
  始化并加载完成。

        ~# demsg
          139.619656 rust_minimal: *Rust minimal sample (init)*
          139.619660 rust_minimal: *Am I built-in? false*

        ~# lsmod | grep rust
        rust_minimal           16384  0

  然后执行 rmmod 把模块卸下:

  ┌────
  │ rmmod rust_minimal
  └────

  再次执行 lsmod就不再看到rust_minimal这个模块了。


6.2 out-of-tree rust模块加载
──────────────

6.2.1 检查 kernel_src 目录的 .o 文件是否存在
╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌

  ┌────
  │ cd kernel_src
  │ du -sh .
  └────

  如下显示编译完成未清理的目录需要保留编译过程的中间文件(*.o),存在这些
  文件大概占用14GB左右的硬盘空间。

         ~# du -sh .
        14G     .

  如果上述文件在之前的编译后被清理,则需要重新回到内核代码目录
  kernel_src 执行 make 命令重新产生中间文件。


6.2.2 拉取 out-of-tree 代码
╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌

  ┌────
  │ git clone -b main
https://github.com/Rust-for-Linux/rust-out-of-tree-module.git
  │ cd rust-out-of-tree-module/
  │ git checkout a5de4e66ae6034fda333ca4fc8a5e301bad7acb4
  └────

  上述项目[6]目前还处于活跃的开发分支中,目前支持的版本已经到了6.2, 但
  没有针对6.1版本的代码指定tag,因此需要手工checkout回支持6.1的最后一个
  版本的commit。


  然后参考该项目的 README.md 文件执行如下命令进行构建:
  ┌────
  │ make KDIR=${LINUX_SOURCE_PATH} LLVM=1
  └────

  这里的 LINUX_SOURCE_PATH 是指你的内核源代码所在的目录,如果你是采用apt
  方式下载 linux-source 的代码的话,这个路径是
  `/usr/src/linux-source-6.1`, 如果是git方式拉取的代码,则是
  `kernel_src` 所在的目录,建议使用绝对路径。正确构建完成后即可看到编译
  出来的 `*.ko` 模块。

        rust-out-of-tree-module# make KDIR=/usr/src/linux-source-6.1 LLVM=1
        make -C /usr/src/linux-source-6.1 M=$PWD
        make[1]: Entering directory '/usr/src/linux-source-6.1'
          RUSTC [M] /usr/src/rust-out-of-tree-module/rust_out_of_tree.o
          MODPOST /usr/src/rust-out-of-tree-module/Module.symvers
          LD [M]  /usr/src/rust-out-of-tree-module/rust_out_of_tree.ko
        make[1]: Leaving directory '/usr/src/linux-source-6.1'

        rust-out-of-tree-module# ls -lh rust_out_of_tree.ko
        -rw-r–r– 1 root root 165K Feb 10 14:15 *rust_out_of_tree.ko*


6.2.3 加载测试
╌╌╌╌╌╌╌╌╌╌╌╌╌╌

  完成后我们就可以看到 `rust_out_of_tree.ko` 这个模块已经构建成功了。
  ┌────
  │ insmod rust_out_of_tree.ko
  └────

  执行完成后执行dmesg查看加载是否成功,出现如下两行信息表示该模块顺利初
  始化并加载完成。

        ~# demsg
         8811.926307 rust_out_of_tree: Rust out-of-tree sample (init)

        ~# lsmod | grep rust
        rust_out_of_tree       16384  0

  然后执行 rmmod 把模块卸下:

  ┌────
  │ rmmod rust_out_of_tree
  └────

  显示如下结果表示卸载成功, 再次执行 lsmod就不再看到rust_out_of_tree这个
  模块了。
        ~# rmmod rust_out_of_tree

        ~# dmesg
        18357.664254 rust_out_of_tree: My numbers are [72, 108, 200]
        18357.664338 rust_out_of_tree: Rust out-of-tree sample (exit)



Footnotes
─────────

[1] <https://www.kernel.org/doc/html/latest/rust/quick-start.html>

[2] <https://www.jianshu.com/p/876b1cca26d8>

[3] <https://apt.llvm.org/>

[4] <https://cateee.net/lkddb/web-lkddb/RUST.html>

[5] <https://www.debian.org/mirror/list.zh-cn.html>

[6] <https://github.com/Rust-for-Linux/rust-out-of-tree-module>

回复