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

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

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

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

書生途 ? 來源:書生途 ? 作者:書生途 ? 2022-05-11 08:55 ? 次閱讀

一,前言

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

Linux 內(nèi)核模塊是一段編譯后的二進(jìn)制代碼,直接插入 Linux 內(nèi)核中,在 Ring 0 (x86–64處理器中執(zhí)行最低和受保護(hù)程度最低的執(zhí)行環(huán))上運(yùn)行。這里的代碼完全不受檢查,但是運(yùn)行速度很快,可以訪問系統(tǒng)中的所有內(nèi)容。

Intel X86架構(gòu)使用了4個(gè)級(jí)別來標(biāo)明不同的特權(quán)級(jí)。 Ring 0 實(shí)際就是內(nèi)核態(tài),擁有最高權(quán)限。而一般應(yīng)用程序處于 Ring 3狀態(tài)--用戶態(tài)。在Linux中,還存在 Ring 1Ring 2 兩個(gè)級(jí)別,一般歸屬驅(qū)動(dòng)程序的級(jí)別。在Windows平臺(tái)沒有 Ring 1Ring 2 兩個(gè)級(jí)別,只用 Ring 0 內(nèi)核態(tài)和 Ring 3 用戶態(tài)。在權(quán)限約束上,高特權(quán)等級(jí)狀態(tài)可以閱讀低特權(quán)等級(jí)狀態(tài)的數(shù)據(jù),例如進(jìn)程上下文、代碼、數(shù)據(jù)等等,但反之則不可。 Ring 0 最高可以讀取 Ring 0-3 所有的內(nèi)容, Ring 1 可以讀 Ring 1-3 的, Ring 2 以此類推, Ring 3 只能讀自己的數(shù)據(jù)。

二,為什么要開發(fā)內(nèi)核模塊

編寫Linux內(nèi)核模塊并不是因?yàn)閮?nèi)核太龐大而不敢修改。直接修改內(nèi)核源碼會(huì)導(dǎo)致很多問題,例如:通過更改內(nèi)核,你將面臨數(shù)據(jù)丟失和系統(tǒng)損壞的風(fēng)險(xiǎn)。內(nèi)核代碼沒有常規(guī)Linux應(yīng)用程序所擁有的安全防護(hù)機(jī)制,如果內(nèi)核發(fā)生故障,將鎖死整個(gè)系統(tǒng)。

更糟糕的是,當(dāng)你修改內(nèi)核并導(dǎo)致錯(cuò)誤后,可能不會(huì)立即表現(xiàn)出來。如果模塊發(fā)生錯(cuò)誤,在其加載時(shí)就鎖定系統(tǒng)是最好的選擇,如果不鎖定,當(dāng)你向模塊中添加更多代碼時(shí),你將會(huì)面臨失控循環(huán)和內(nèi)存泄漏的風(fēng)險(xiǎn),如果不小心,它們會(huì)隨著計(jì)算機(jī)繼續(xù)運(yùn)行而持續(xù)增長(zhǎng),最終,關(guān)鍵的存儲(chǔ)器結(jié)構(gòu)甚至緩沖區(qū)都可能被覆蓋。

編寫內(nèi)核模塊時(shí),基本是可以丟棄傳統(tǒng)的應(yīng)用程序開發(fā)范例。除了加載和卸載模塊之外,你還需要編寫響應(yīng)系統(tǒng)事件的代碼(而不是按順序模式執(zhí)行的代碼)。通過內(nèi)核開發(fā),你正在編寫API,而不是應(yīng)用程序。

你也無權(quán)訪問標(biāo)準(zhǔn)庫(kù),雖然內(nèi)核提供了一些函數(shù),例如 printk (可替代printf)和 kmalloc (以與malloc相似的方式運(yùn)行),但你在很大程度上只能使用自己的設(shè)備。此外,在卸載模塊時(shí),你需要將自己清理干凈,系統(tǒng)不會(huì)在你的模塊被卸載后進(jìn)行垃圾回收。

三,開發(fā)的工具準(zhǔn)備

開始編寫Linux內(nèi)核模塊之前,我們首先要準(zhǔn)備一些工具。最重要的是,你需要有一臺(tái)Linux機(jī)器,盡管可以使用任何Linux發(fā)行版,但本文中,我使用的是Ubuntu 16.04 LTS,如果你使用的其他發(fā)行版,可能需要稍微調(diào)整安裝命令。

其次,你需要一臺(tái)物理機(jī)或虛擬機(jī),我不建議你直接使用物理機(jī)編寫內(nèi)核模塊,因?yàn)楫?dāng)你出錯(cuò)時(shí),主機(jī)的數(shù)據(jù)可能會(huì)丟失。在編寫和調(diào)試內(nèi)核模塊的過程中,你至少會(huì)鎖定機(jī)器幾次,內(nèi)核崩潰時(shí),最新的代碼更改可能仍在寫緩沖區(qū)中,因此,你的源文件可能會(huì)損壞,在虛擬機(jī)中進(jìn)行測(cè)試可以避免這種風(fēng)險(xiǎn)。

最后,你至少需要了解一些C。對(duì)于內(nèi)核來說,C++在運(yùn)行時(shí)太大了,因此編寫純C代碼是必不可少的。另外,對(duì)于其與硬件的交互,了解一些組件可能會(huì)有所幫助。

四,安裝開發(fā)環(huán)境

讓我們開始編寫一些代碼,準(zhǔn)備環(huán)境:

mkdir -p ?/src/lkm_example
cd ?/src/lkm_example

啟動(dòng)您喜歡的編輯器(在我的例子中是vim),并創(chuàng)建具有以下內(nèi)容的文件 lkm_example.c

#include 
#include 
#include 

MODULE_LICENSE("GPL");
MODULE_AUTHOR("abin");
MODULE_DESCRIPTION("A simple example Linux module.");
MODULE_VERSION("0.01");

static int __init lkm_example_init(void) {
 printk(KERN_INFO "Hello, World!\n");
 return 0;
}
static void __exit lkm_example_exit(void) {
 printk(KERN_INFO "Goodbye, World!\n");
}
module_init(lkm_example_init);
module_exit(lkm_example_exit);

現(xiàn)在,我們已經(jīng)構(gòu)建了最簡(jiǎn)單的內(nèi)核模塊,下面介紹代碼的細(xì)節(jié):

includes" 包括Linux內(nèi)核開發(fā)所需的必需頭文件。

根據(jù)模塊的許可證,可以將 MODULE_LICENSE 設(shè)置為各種值。要查看完整列表,請(qǐng)運(yùn)行:

grep "MODULE_LICENSE" -B 27 /usr/src/linux-headers-`uname -r`/include/linux/module.h

我們將 init (加載)和 exit (卸載)函數(shù)都定義為靜態(tài)并返回 int 。

注意使用 printk 而不是 printf ,另外, printkprintf 共享的參數(shù)也不相同。例如, KERN_INFO 是一個(gè)標(biāo)志,用于聲明應(yīng)為該行設(shè)置的日志記錄優(yōu)先級(jí),并且不帶逗號(hào)。內(nèi)核在printk函數(shù)中對(duì)此進(jìn)行分類以節(jié)省堆棧內(nèi)存。

在文件末尾,我們調(diào)用 module_initmodule_exit 函數(shù)告訴內(nèi)核哪些函數(shù)是內(nèi)核模塊的加載和卸載函數(shù)。這使我們可以任意命名這兩個(gè)函數(shù)。

目前,還無法編譯此文件,我們需要一個(gè) Makefile ,請(qǐng)注意, make 對(duì)于空格和制表符敏感,因此請(qǐng)確保在適當(dāng)?shù)牡胤绞褂弥票矸皇强崭瘛?/p>

obj-m += lkm_example.o
all:
 make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
 make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

如果我們運(yùn)行 "make",它將成功編譯你編寫的模塊,編譯后的文件為 "lkm_example.ko",如果收到任何錯(cuò)誤,請(qǐng)檢查示例源文件中的引號(hào)是否正確,并且不要將其粘貼為UTF-8字符。

現(xiàn)在我們可以將此模塊加載進(jìn)內(nèi)核進(jìn)行測(cè)試了,命令如下:

sudo insmod lkm_example.ko

如果一切順利,你將看不到任何輸出,因?yàn)?printk 函數(shù)不會(huì)輸出到控制臺(tái),而是輸出到內(nèi)核日志。要看到內(nèi)核日志中的內(nèi)容,我們需要運(yùn)行:

sudo dmesg

你應(yīng)該看到以時(shí)間戳為前綴的行:"Hello, World!",這意味著我們的內(nèi)核模塊已加載并成功打印到內(nèi)核日志中。

我們還可以檢查模塊是否已被加載:

lsmod | grep "lkm_example"

要卸載模塊,運(yùn)行:

sudo rmmod lkm_example

如果再次運(yùn)行 dmesg ,你將看到"Goodbye, World!" 在日志中。你也可以再次使用 lsmod 命令確認(rèn)它已卸載。

如你所見,此測(cè)試工作流程有點(diǎn)繁瑣,因此要使其自動(dòng)化,我們可以在 Makefile 中添加:

test:
 sudo dmesg -C
 sudo insmod lkm_example.ko
 sudo rmmod lkm_example.ko
 dmesg

現(xiàn)在,運(yùn)行:

make test

測(cè)試我們的模塊并查看內(nèi)核日志的輸出,而不必運(yùn)行單獨(dú)的命令。

現(xiàn)在,我們有了一個(gè)功能齊全,但又很簡(jiǎn)單的內(nèi)核模塊!

六,一般模塊

讓我們?cè)偎伎枷?。盡管內(nèi)核模塊可以完成各種任務(wù),但與應(yīng)用程序進(jìn)行交互是其最常見的用途之一。

由于操作系統(tǒng)限制了應(yīng)用程序查看內(nèi)核空間內(nèi)存的內(nèi)容,因此,應(yīng)用程序必須使用API與內(nèi)核進(jìn)行通信。盡管從技術(shù)上講,有多種方法可以完成此操作,但最常見的方法是創(chuàng)建設(shè)備文件。

你以前可能已經(jīng)與設(shè)備文件進(jìn)行過交互。使用 /dev/zero , /dev/null 或類似設(shè)備的命令就是與名為 zero 和 null 的設(shè)備進(jìn)行交互,這些設(shè)備將返回期望的值。

在我們的示例中,我們將返回 "Hello,World",雖然這些字符串對(duì)于應(yīng)用程序并沒有什么用,但它將顯示通過設(shè)備文件響應(yīng)應(yīng)用程序的過程。

這是完整代碼:

#include 
#include 
#include 
#include 
#include 

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Robert W. Oliver II");
MODULE_DESCRIPTION("A simple example Linux module.");
MODULE_VERSION("0.01");

#define DEVICE_NAME "lkm_example"
#define EXAMPLE_MSG "Hello, World!\n"
#define MSG_BUFFER_LEN 15

/* Prototypes for device functions */
static int device_open(struct inode *, struct file *);
static int device_release(struct inode *, struct file *);
static ssize_t device_read(struct file *, char *, size_t, loff_t *);
static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
               
static int major_num;
static int device_open_count = 0;
static char msg_buffer[MSG_BUFFER_LEN];
static char *msg_ptr;
               
/* This structure points to all of the device functions */
static struct file_operations file_ops = {
 .read = device_read,
 .write = device_write,
 .open = device_open,
 .release = device_release
};
               
/* When a process reads from our device, this gets called. */
static ssize_t device_read(struct file *flip, char *buffer, size_t len, loff_t *offset) {
 int bytes_read = 0;
  /* If we’re at the end, loop back to the beginning */
  if (*msg_ptr == 0) {
   msg_ptr = msg_buffer;
  }
  /* Put data in the buffer */
  while (len && *msg_ptr) {
    /* Buffer is in user data, not kernel, so you can’t just reference
     * with a pointer. The function put_user handles this for us */
    put_user(*(msg_ptr++), buffer++);
    len--;
    bytes_read++;
 }
  return bytes_read;
}

/* Called when a process tries to write to our device */
static ssize_t device_write(struct file *flip, const char *buffer, size_t len, loff_t *offset) {
 /* This is a read-only device */
  printk(KERN_ALERT "This operation is not supported.\n");
  return -EINVAL;
}
         
/* Called when a process opens our device */
static int device_open(struct inode *inode, struct file *file) {
  /* If device is open, return busy */
  if (device_open_count) {
   return -EBUSY;
  }
  device_open_count++;
  try_module_get(THIS_MODULE);
  return 0;
}
         
/* Called when a process closes our device */
static int device_release(struct inode *inode, struct file *file) {
  /* Decrement the open counter and usage count. Without this, the module would not unload. */
  device_open_count--;
  module_put(THIS_MODULE);
  return 0;
}
         
static int __init lkm_example_init(void) {
  /* Fill buffer with our message */
  strncpy(msg_buffer, EXAMPLE_MSG, MSG_BUFFER_LEN);
  /* Set the msg_ptr to the buffer */
  msg_ptr = msg_buffer;
  /* Try to register character device */
  major_num = register_chrdev(0, "lkm_example", &file_ops);
  if (major_num < 0) {
   printk(KERN_ALERT "Could not register device: %d\n", major_num);
   return major_num;
  } else {
   printk(KERN_INFO "lkm_example module loaded with device major number %d\n", major_num);
   return 0;
  }
}

static void __exit lkm_example_exit(void) {
  /* Remember — we have to clean up after ourselves. Unregister the character device. */
  unregister_chrdev(major_num, DEVICE_NAME);
  printk(KERN_INFO "Goodbye, World!\n");
}

/* Register module functions */
module_init(lkm_example_init);
module_exit(lkm_example_exit);

既然我們的示例所做的不僅僅是在加載和卸載時(shí)打印一條消息,讓我們修改Makefile,使其僅加載模塊而不卸載模塊:

obj-m += lkm_example.o
all:
  make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
 make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
test:
  # We put a — in front of the rmmod command to tell make to ignore
  # an error in case the module isn’t loaded.
  -sudo rmmod lkm_example
  # Clear the kernel log without echo
  sudo dmesg -C
  # Insert the module
  sudo insmod lkm_example.ko
  # Display the kernel log
  dmesg

現(xiàn)在,當(dāng)您運(yùn)行 "make test" 時(shí),您將看到設(shè)備主號(hào)碼的輸出。在我們的示例中,這是由內(nèi)核自動(dòng)分配的,但是,你需要此值來創(chuàng)建設(shè)備。

獲取從 "make test" 獲得的值,并使用它來創(chuàng)建設(shè)備文件,以便我們可以從用戶空間與內(nèi)核模塊進(jìn)行通信:

sudo mknod /dev/lkm_example c MAJOR 0

在上面的示例中,將MAJOR替換為你運(yùn)行 "make test" 或 "dmesg" 后得到的值,我得到的MAJOR為236,如上圖,mknod命令中的 "c" 告訴mknod我們需要?jiǎng)?chuàng)建一個(gè)字符設(shè)備文件。

現(xiàn)在我們可以從設(shè)備中獲取內(nèi)容:

cat /dev/lkm_example

或者通過 "dd" 命令:

dd if=/dev/lkm_example of=test bs=14 count=100

你也可以通過應(yīng)用程序訪問此設(shè)備,它們不必編譯應(yīng)用程序--甚至Python、Ruby和PHP腳本也可以訪問這些數(shù)據(jù)。

完成測(cè)試后,將其刪除并卸載模塊:

sudo rm /dev/lkm_example
sudo rmmod lkm_example

七,總結(jié)

盡管我提供的示例是簡(jiǎn)單內(nèi)核模塊,但你完全可以根據(jù)此結(jié)構(gòu)來構(gòu)造自己的模塊,以完成非常復(fù)雜的任務(wù)。

請(qǐng)記住,你在內(nèi)核模塊開發(fā)過程中完全靠自己。如果你為客戶提供一個(gè)項(xiàng)目的報(bào)價(jià),一定要把預(yù)期的調(diào)試時(shí)間增加一倍,甚至三倍。內(nèi)核代碼必須盡可能的完美,以確保運(yùn)行它的系統(tǒng)的完整性和可靠性。

審核編輯:湯梓紅

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

    關(guān)注

    87

    文章

    11171

    瀏覽量

    208478
  • 編寫
    +關(guān)注

    關(guān)注

    0

    文章

    29

    瀏覽量

    8416
  • 內(nèi)核模塊
    +關(guān)注

    關(guān)注

    0

    文章

    10

    瀏覽量

    3078
收藏 人收藏

    評(píng)論

    相關(guān)推薦

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

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

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

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

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

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

    RZ/G2L Linux系統(tǒng)如何添加新的內(nèi)核模塊

    RZ/G2L Linux系統(tǒng)的鏡像基于yocto構(gòu)建,本篇介紹如何添加新的內(nèi)核模塊。
    的頭像 發(fā)表于 01-04 12:19 ?1611次閱讀
    RZ/G2L <b class='flag-5'>Linux</b>系統(tǒng)如何添加新的<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)核模塊編譯

    (description);三、Linux內(nèi)核模塊的編譯首先為HelloWorld模塊編寫MakeFile文件該MakeFile文件應(yīng)該與源碼位于同一目錄在Makefile中,在obj
    發(fā)表于 09-24 09:11

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

    Linux設(shè)備驅(qū)動(dòng)開發(fā)詳解》第4章、Linux內(nèi)核模塊
    發(fā)表于 10-27 14:15 ?0次下載
    《<b class='flag-5'>Linux</b>設(shè)備驅(qū)動(dòng)開發(fā)<b class='flag-5'>詳解</b>》第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 ?4436次閱讀
    <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 Kernel Module,LKM),我們簡(jiǎn)稱為模塊
    發(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 ?3177次閱讀

    嵌入式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 ?4250次閱讀

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

    嵌入式LINUX系統(tǒng)內(nèi)核內(nèi)核模塊調(diào)試(嵌入式開發(fā)和硬件開發(fā))-嵌入式LINUX系統(tǒng)內(nèi)核內(nèi)核模塊
    發(fā)表于 07-30 13:55 ?10次下載
    嵌入式<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 ?1986次閱讀

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

    ,需要了解Linux內(nèi)核的基本概念和API。以下是一些關(guān)鍵概念: 1.1 內(nèi)核模塊Linux內(nèi)核模塊是一種動(dòng)態(tài)加載和卸載的代碼,可以在不重
    的頭像 發(fā)表于 08-30 15:02 ?287次閱讀