0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

基于Rust基礎(chǔ)上如何去編寫一個(gè)Linux內(nèi)核模塊

開(kāi)關(guān)電源芯片 ? 來(lái)源:Linux中國(guó) ? 作者:蘇子彬 ? 2021-08-27 09:59 ? 次閱讀

編者按:近些年來(lái) Rust 語(yǔ)言由于其內(nèi)存安全性和性能等優(yōu)勢(shì)得到了很多關(guān)注,尤其是 Linux 內(nèi)核也在準(zhǔn)備將其集成到其中,因此,我們特邀阿里云工程師蘇子彬?yàn)槲覀兘榻B一下如何在 Linux 內(nèi)核中集成 Rust 支持。

2021 年 4 月 14 號(hào),一封主題名為《Rust support[1]》的郵件出現(xiàn)在 LKML 郵件組中。這封郵件主要介紹了向內(nèi)核引入 Rust 語(yǔ)言支持的一些看法以及所做的工作。郵件的發(fā)送者是 Miguel Ojeda[2],為內(nèi)核中 Compiler attributes、.clang-format 等多個(gè)模塊的維護(hù)者,也是目前 Rust for Linux 項(xiàng)目的維護(hù)者。

Rust for Linux 項(xiàng)目目前得到了 Google 的大力支持[3],Miguel Ojeda[4] 當(dāng)前的全職工作就是負(fù)責(zé) Rust for Linux 項(xiàng)目。

長(zhǎng)期以來(lái),內(nèi)核使用 C 語(yǔ)言和匯編語(yǔ)言作為主要的開(kāi)發(fā)語(yǔ)言,部分輔助語(yǔ)言包括 Python、Perl、shell 被用來(lái)進(jìn)行代碼生成、打補(bǔ)丁、檢查等工作。2016 年 Linux 25 歲生日時(shí),在對(duì) Linus Torvalds 的一篇 采訪[5]中,他就曾表示過(guò):

這根本不是一個(gè)新現(xiàn)象。我們有過(guò)使用 Modula-2 或 Ada 的系統(tǒng)人員,我不得不說(shuō) Rust 看起來(lái)比這兩個(gè)災(zāi)難要好得多。

我對(duì) Rust 用于操作系統(tǒng)內(nèi)核并不信服(雖然系統(tǒng)編程不僅限于內(nèi)核),但同時(shí),毫無(wú)疑問(wèn),C 有很多局限性。

最新的對(duì) Rust support[6] 的 RFC 郵件的回復(fù)中,他更是說(shuō):

所以我對(duì)幾個(gè)個(gè)別補(bǔ)丁做了回應(yīng),但總體上我不討厭它。

沒(méi)有用他特有的回復(fù)方式來(lái)反擊,應(yīng)該就是暗自喜歡了吧。

目前 Rust for Linux 依然是一個(gè)獨(dú)立于上游的項(xiàng)目,并且主要工作還集中的驅(qū)動(dòng)接口相關(guān)的開(kāi)發(fā)上,并非一個(gè)完善的項(xiàng)目。

項(xiàng)目地址:https://github.com/Rust-for-Linux/linux

為什么是 Rust

在 Miguel Ojeda[7] 的第一個(gè) RFC 郵件中,他已經(jīng)提到了 “Why Rust”,簡(jiǎn)單總結(jié)下:

在安全子集safe subset中不存在未定義行為,包括內(nèi)存安全和數(shù)據(jù)競(jìng)爭(zhēng);

更加嚴(yán)格的類型檢測(cè)系統(tǒng)能夠進(jìn)一步減少邏輯錯(cuò)誤;

明確區(qū)分 safe 和 unsafe 代碼;

更加面向未來(lái)的語(yǔ)言:sum 類型、模式匹配、泛型、RAII、生命周期、共享及專屬引用、模塊與可見(jiàn)性等等;

可擴(kuò)展的獨(dú)立標(biāo)準(zhǔn)庫(kù);

集成的開(kāi)箱可用工具:文檔生成、代碼格式化、linter 等,這些都基于編譯器本身。

編譯支持 Rust 的內(nèi)核

根據(jù) Rust for Linux 文檔[8],編譯一個(gè)包含 Rust 支持的內(nèi)核需要如下步驟:

安裝 rustc 編譯器。Rust for Linux 不依賴 cargo,但需要最新的 beta 版本的 rustc。使用 rustup命令安裝:

rustup default beta-2021-06-23

安裝 Rust 標(biāo)準(zhǔn)庫(kù)的源碼。Rust for Linux 會(huì)交叉編譯 Rust 的 core 庫(kù),并將這兩個(gè)庫(kù)鏈接進(jìn)內(nèi)核鏡像。

rustup component add rust-src

安裝 libclang 庫(kù)。libclang 被 bindgen 用做前端,用來(lái)處理 C 代碼。libclang 可以從 llvm 官方主頁(yè)[9] 下載預(yù)編譯好的版本。

安裝 bindgen 工具,bindgen 是一個(gè)自動(dòng)將 C 接口轉(zhuǎn)為 RustFFI 接口的庫(kù):

cargo install --locked --version 0.56.0 bindgen

克隆最新的 Rust for Linux 代碼:

git clone https://github.com/Rust-for-Linux/linux.git

配置內(nèi)核啟用 Rust 支持:

Kernel hacking -》 Sample kernel code -》 Rust samples

構(gòu)建:

LIBCLANG_PATH=/path/to/libclang make -j LLVM=1 bzImage

這里我們使用 clang 作為默認(rèn)的內(nèi)核編譯器,使用 gcc 理論上是可以的,但還處于 早期實(shí)驗(yàn)[10] 階段。

Rust 是如何集成進(jìn)內(nèi)核的

目錄結(jié)構(gòu)

為了將 Rust 集成進(jìn)內(nèi)核中,開(kāi)發(fā)者首先對(duì) Kbuild 系統(tǒng)進(jìn)行修改,加入了相關(guān)配置項(xiàng)來(lái)開(kāi)啟/關(guān)閉 Rust 的支持。

此外,為了編譯 rs 文件,添加了一些 Makefile 的規(guī)則。這些修改分散在內(nèi)核目錄中的不同文件里。

Rust 生成的目標(biāo)代碼中的符號(hào)會(huì)因?yàn)?Mangling 導(dǎo)致其長(zhǎng)度超過(guò)同樣的 C 程序所生成符號(hào)的長(zhǎng)度,因此,需要對(duì)內(nèi)核的符號(hào)長(zhǎng)度相關(guān)的邏輯進(jìn)行補(bǔ)丁。開(kāi)發(fā)者引入了 “大內(nèi)核符號(hào)”的概念,用來(lái)在保證向前兼容的情況下,支持 Rust 生成的目標(biāo)文件符號(hào)長(zhǎng)度。

其他 Rust 相關(guān)的代碼都被放置在了 rust 目錄下。

在 Rust 中使用 C 函數(shù)

Rust 提供 FFI(外部函數(shù)接口Foreign Function Interface)用來(lái)支持對(duì) C 代碼的調(diào)用。Bindgen[11] 是一個(gè) Rust 官方的工具,用來(lái)自動(dòng)化地從 C 函數(shù)中生成 Rust 的 FFI 綁定。內(nèi)核中的 Rust 也使用該工具從原生的內(nèi)核 C 接口中生成 Rust 的 FFI 綁定。

quiet_cmd_bindgen = BINDGEN $@ cmd_bindgen = $(BINDGEN) $《 $(shell grep -v ‘^#|^$$’ $(srctree)/rust/bindgen_parameters) --use-core --with-derive-default --ctypes-prefix c_types --no-debug ‘.*’ --size_t-is-usize -o $@ -- $(bindgen_c_flags_final) -DMODULE$(objtree)/rust/bindings_generated.rs: $(srctree)/rust/kernel/bindings_helper.h $(srctree)/rust/bindgen_parameters FORCE $(call if_changed_dep,bindgen)

ABI

Rust 相關(guān)的代碼會(huì)單獨(dú)從 rs 編譯為 .o,生成的目標(biāo)文件是標(biāo)準(zhǔn)的 ELF 文件。在鏈接階段,內(nèi)核的鏈接器將 Rust 生成的目標(biāo)文件與其他 C 程序生成的目標(biāo)文件一起鏈接為內(nèi)核鏡像文件。因此,只要 Rust 生成的目標(biāo)文件 ABI 與 C 程序的一致,就可以無(wú)差別的被鏈接(當(dāng)然,被引用的符號(hào)還是要存在的)。

Rust 的 alloc 與 core 庫(kù)

目前 Rust for Linux 依賴于 core 庫(kù)。在 core 中定義了基本的 Rust 數(shù)據(jù)結(jié)構(gòu)與語(yǔ)言特性,例如熟悉的 Option《》 和 Result《》 就是 core 庫(kù)所提供。

這個(gè)庫(kù)被交叉編譯后被直接鏈接進(jìn)內(nèi)核鏡像文件,這也是導(dǎo)致啟用 Rust 的內(nèi)核鏡像文件尺寸較大的原因。在未來(lái)的工作中,這兩個(gè)庫(kù)會(huì)被進(jìn)一步被優(yōu)化,去除掉某些無(wú)用的部分,例如浮點(diǎn)操作,Unicode 相關(guān)的內(nèi)容,F(xiàn)utures 相關(guān)的功能等。

之前的 Rust for Linux 項(xiàng)目還依賴于 Rust 的 alloc 庫(kù)。Rust for Linux 定義了自己的 GlobalAlloc 用來(lái)管理基本的堆內(nèi)存分配。主要被用來(lái)進(jìn)行堆內(nèi)存分配,并且使用 GFP_KERNEL 標(biāo)識(shí)作為默認(rèn)的內(nèi)存分配模式。

不過(guò)在在最新的 拉取請(qǐng)求[12] 中,社區(qū)已經(jīng)將移植并修改了 Rust的 alloc 庫(kù),使其能夠在盡量保證與 Rust 上游統(tǒng)一的情況下,允許開(kāi)發(fā)者定制自己的內(nèi)存分配器。不過(guò)目前使用自定義的 GFP_ 標(biāo)識(shí)來(lái)分配內(nèi)存依然是不支持的,但好消息是這個(gè)功能正在開(kāi)發(fā)中。

“Hello World” 內(nèi)核模塊

用一個(gè)簡(jiǎn)單的 Hello World 來(lái)展示如何使用 Rust 語(yǔ)言編寫驅(qū)動(dòng)代碼,hello_world.rs:

#?。踤o_std]#?。踗eature(allocator_api, global_asm)]use kernel::*;module! { type: HelloWorld, name: b“hello_world”, author: b“d0u9”, description: b“A simple hello world example”, license: b“GPL v2”,}struct HelloWorld;impl KernelModule for HelloWorld { fn init() -》 Result《Self》 { pr_info!(“Hello world from rust!

”); Ok(HelloWorld) }}impl Drop for HelloWorld { fn drop(&mut self) { pr_info?。ā癇ye world from rust!

”); }}

與之對(duì)應(yīng)的 Makefile:

obj-m := hello_world.o

構(gòu)建:

make -C /path/to/linux_src M=$(pwd) LLVM=1 modules

之后就和使用普通的內(nèi)核模塊一樣,使用 insmod 工具或者 modprobe 工具加載就可以了。在使用體驗(yàn)上是沒(méi)有區(qū)別的。

module! { } 宏

這個(gè)宏可以被認(rèn)為是 Rust 內(nèi)核模塊的入口,因?yàn)樵谄渲卸x了一個(gè)內(nèi)核模塊所需的所有信息,包括:Author、License、Description 等。其中最重要的是 type 字段,在其中需要指定內(nèi)核模塊結(jié)構(gòu)的名字。在這個(gè)例子中:

module! { 。。。 type: HelloWorld, 。。。}struct HelloWorld;

module_init() 與 module_exit()

在使用 C 編寫的內(nèi)核模塊中,這兩個(gè)宏定義了模塊的入口函數(shù)與退出函數(shù)。在 Rust 編寫的內(nèi)核模塊中,對(duì)應(yīng)的功能由 trait KernelModule 和 trait Drop 來(lái)實(shí)現(xiàn)。trait KernelModule 中定義 init() 函數(shù),會(huì)在模塊驅(qū)動(dòng)初始化時(shí)被調(diào)用;trait Drop 是 Rust 的內(nèi)置 trait,其中定義的 drop() 函數(shù)會(huì)在變量生命周期結(jié)束時(shí)被調(diào)用。

編譯與鏈接

所有的內(nèi)核模塊文件會(huì)首先被編譯成 .o 目標(biāo)文件,之后由內(nèi)核鏈接器將這些 .o 文件和自動(dòng)生成的模塊目標(biāo)文件 .mod.o 一起鏈接成為 .ko 文件。這個(gè) .ko 文件符合動(dòng)態(tài)庫(kù) ELF 文件格式,能夠被內(nèi)核識(shí)別并加載。

其他

完整的介紹 Rust 是如何被集成進(jìn)內(nèi)核的文章可以在 我的 Github[13] 上找到,由于寫的倉(cāng)促,可能存在一些不足,還請(qǐng)見(jiàn)諒。

作者:蘇子彬,阿里云 PAI 平臺(tái)開(kāi)發(fā)工程師,主要從事 Linux 系統(tǒng)及驅(qū)動(dòng)的相關(guān)開(kāi)發(fā),曾為 PAI 平臺(tái)編寫 FPGA 加速卡驅(qū)動(dòng)。

參考資料

[1]Rust support:https://lkml.org/lkml/2021/4/14/1023

[2]Miguel Ojeda:https://ojeda.dev/

[3]Google 的大力支持:https://www.zdnet.com/article/rust-in-the-linux-kernel-just-got-a-big-boost-from-google/

[4]Miguel Ojeda:https://ojeda.dev/

[5]采訪:https://www.infoworld.com/article/3109150/linux-at-25-linus-torvalds-on-the-evolution-and-future-of-linux.html

[6]Rust support:https://lkml.org/lkml/2021/4/14/1023

[7]Miguel Ojeda:https://ojeda.dev/

[8]Rust for Linux 文檔:https://github.com/Rust-forLinux/linux/blob/rust/Documentation/rust/quick-start.rst

[9]llvm 官方主頁(yè):https://github.com/llvm/llvm-project/releases

[10]早期實(shí)驗(yàn):https://github.com/Rust-for-Linux/linux/blob/rust/Documentation/rust/quick-start.rst#building

[11]Bindgen:https://github.com/rust-lang/rust-bindgen

[12]拉取請(qǐng)求:https://github.com/Rust-for-Linux/linux/pull/402

[13]我的 Github:https://github.com/d0u9/Linux-Device-Driver-Rust/tree/master/00_Introduction_to_Rust_Module_in_Linux

編輯:jq

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • Google
    +關(guān)注

    關(guān)注

    5

    文章

    1748

    瀏覽量

    57187
  • python
    +關(guān)注

    關(guān)注

    53

    文章

    4753

    瀏覽量

    84078
  • C 語(yǔ)言
    +關(guān)注

    關(guān)注

    0

    文章

    18

    瀏覽量

    14089
  • Rust
    +關(guān)注

    關(guān)注

    1

    文章

    226

    瀏覽量

    6497

原文標(biāo)題:如何用 Rust 編寫一個(gè) Linux 內(nèi)核模塊

文章出處:【微信號(hào):gh_3980db2283cd,微信公眾號(hào):開(kāi)關(guān)電源芯片】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    linux 了解內(nèi)核模塊的原理 《Rice linux 學(xué)習(xí)開(kāi)發(fā)》

    內(nèi)核模塊種沒(méi)有經(jīng)過(guò)鏈接,不能獨(dú)立運(yùn)行的目標(biāo)文件,是在內(nèi)核空間中運(yùn)行的程序。
    的頭像 發(fā)表于 07-16 10:08 ?4552次閱讀
    <b class='flag-5'>linux</b> 了解<b class='flag-5'>內(nèi)核模塊</b>的原理 《Rice <b class='flag-5'>linux</b> 學(xué)習(xí)開(kāi)發(fā)》

    Linux 內(nèi)核模塊工作原理及內(nèi)核模塊編譯案例

    個(gè)內(nèi)核模塊至少包含兩個(gè)函數(shù),模塊被加載時(shí)執(zhí)行的初始化函數(shù)init_module()和模塊被卸載
    發(fā)表于 09-23 09:39 ?2421次閱讀
    <b class='flag-5'>Linux</b> <b class='flag-5'>內(nèi)核模塊</b>工作原理及<b class='flag-5'>內(nèi)核模塊</b>編譯案例

    詳解Linux內(nèi)核模塊編寫方法

    Linux 系統(tǒng)為應(yīng)用程序提供了功能強(qiáng)大且容易擴(kuò)展的 API,但在某些情況下,這還遠(yuǎn)遠(yuǎn)不夠。與硬件交互或進(jìn)行需要訪問(wèn)系統(tǒng)中特權(quán)信息的操作時(shí),就需要個(gè)內(nèi)核模塊。
    的頭像 發(fā)表于 05-11 08:55 ?3660次閱讀

    Linux內(nèi)核模塊間通訊方法

    Linux內(nèi)核模塊間通訊方法非常的多,最便捷的方法莫過(guò)于函數(shù)或變量符號(hào)導(dǎo)出,然后直接調(diào)用。默認(rèn)情況下,模塊模塊之間、模塊
    發(fā)表于 06-07 16:23 ?2245次閱讀
    <b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核模塊</b>間通訊方法

    Linux內(nèi)核模塊程序結(jié)構(gòu)

    Linux設(shè)備驅(qū)動(dòng)會(huì)以內(nèi)核模塊的形式出現(xiàn),因此,學(xué)會(huì)編寫Linux內(nèi)核模塊編程是學(xué)習(xí)Linux設(shè)
    發(fā)表于 05-27 09:36

    高效學(xué)習(xí)Linux內(nèi)核——內(nèi)核模塊編譯

    內(nèi)核是世界最大的開(kāi)源項(xiàng)目之,但是內(nèi)核是什么,它用于什么?、什么是linux
    發(fā)表于 09-24 09:11

    Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)詳解》第4章、Linux內(nèi)核模塊

    Linux設(shè)備驅(qū)動(dòng)開(kāi)發(fā)詳解》第4章、Linux內(nèi)核模塊
    發(fā)表于 10-27 14:15 ?0次下載
    《<b class='flag-5'>Linux</b>設(shè)備驅(qū)動(dòng)開(kāi)發(fā)詳解》第4章、<b class='flag-5'>Linux</b><b class='flag-5'>內(nèi)核模塊</b>

    內(nèi)核模塊的原理以及其模塊編寫

    內(nèi)核模塊是具有獨(dú)立功能的程序。它可以被單獨(dú)編譯,但是不能單獨(dú)運(yùn)行,它的運(yùn)行必須被鏈接到內(nèi)核作為內(nèi)核一部分在內(nèi)核空間中運(yùn)行。
    的頭像 發(fā)表于 01-02 11:11 ?4365次閱讀
    <b class='flag-5'>內(nèi)核模塊</b>的原理以及其<b class='flag-5'>模塊</b><b class='flag-5'>編寫</b>

    什么是內(nèi)核模塊?如何編寫個(gè)簡(jiǎn)單的模塊?

    內(nèi)核模塊Linux內(nèi)核向外部提供的個(gè)插口,其全稱為動(dòng)態(tài)可加載內(nèi)核模塊(Loadable Ke
    發(fā)表于 08-24 17:15 ?20次下載

    什么是 Linux 內(nèi)核模塊?

    lsmod 命令能夠告訴你當(dāng)前系統(tǒng)加載了哪些內(nèi)核模塊,以及關(guān)于使用它們的些有趣的細(xì)節(jié)。
    的頭像 發(fā)表于 08-09 17:01 ?3140次閱讀

    嵌入式LINUX系統(tǒng)內(nèi)核內(nèi)核模塊調(diào)試教程

    本文檔的主要內(nèi)容詳細(xì)介紹的是嵌入式LINUX系統(tǒng)內(nèi)核內(nèi)核模塊調(diào)試教程。
    發(fā)表于 11-06 17:32 ?21次下載
    嵌入式<b class='flag-5'>LINUX</b>系統(tǒng)<b class='flag-5'>內(nèi)核</b>和<b class='flag-5'>內(nèi)核模塊</b>調(diào)試教程

    如何在Petalinux創(chuàng)建Linux內(nèi)核模塊

    --enable”,能創(chuàng)建Linux內(nèi)核模塊,包括c源代碼文件,Makefile,Yocto的bb文件。相關(guān)文件放在目錄“ project-spec / meta-user / recipes-modules”中
    的頭像 發(fā)表于 03-02 11:10 ?4180次閱讀

    嵌入式LINUX系統(tǒng)內(nèi)核內(nèi)核模塊調(diào)試

    嵌入式LINUX系統(tǒng)內(nèi)核內(nèi)核模塊調(diào)試(嵌入式開(kāi)發(fā)和硬件開(kāi)發(fā))-嵌入式LINUX系統(tǒng)內(nèi)核內(nèi)核模塊
    發(fā)表于 07-30 13:55 ?9次下載
    嵌入式<b class='flag-5'>LINUX</b>系統(tǒng)<b class='flag-5'>內(nèi)核</b>和<b class='flag-5'>內(nèi)核模塊</b>調(diào)試

    Linux內(nèi)核模塊參數(shù)傳遞與sysfs文件系統(tǒng)

    函數(shù)傳參的內(nèi)核傳參機(jī)制,編寫內(nèi)核程序時(shí)只要實(shí)現(xiàn)傳參接口,用戶在加載內(nèi)核模塊時(shí)即可傳入指定參數(shù),使得內(nèi)核模塊更加靈活。
    發(fā)表于 06-07 16:23 ?1915次閱讀

    linux驅(qū)動(dòng)程序如何加載進(jìn)內(nèi)核

    ,需要了解Linux內(nèi)核的基本概念和API。以下是些關(guān)鍵概念: 1.1 內(nèi)核模塊Linux內(nèi)核模塊
    的頭像 發(fā)表于 08-30 15:02 ?191次閱讀