linux內核編程入門 hello world


注意:

  • Makefile 文件的命名注意M需要大寫,否則會報錯。
  • 在Makefile文件中make命令前應為tab制表符。

下文轉載至:https://blog.csdn.net/bingqing07/article/details/5888875

首先得了解一下什么是模塊: 模塊是具有獨立功能的程序,它可以被單獨編譯,但不能獨立運行。它在運行時被鏈接到內核作為內核的一部分在內核空間運行,這與運行在用戶空間的進程是不同的。模塊通常由一組函數和數據結構組成,用來實現一種文件系統、一個驅動程序或其他內核上層的功能。

這樣說吧,模塊就是整個內核的一部分。但是跟C程序中函數不一樣的一點是,內核模塊可以在它所認為適當的時候,插入到內核或者從內核中刪除,而且還不影響內核的正常運行。從而可以在必要的時候對內核進行裁剪,這樣能夠更好的適應於用戶的需求。

廢話少說了。我們現在就開始進入內核編寫的階段,看一看怎么樣將一個C程序一步步變成相應的內核模塊。

每一個學習過編程語言的人都知道,第一個示例程序肯定是hello world。我們內核編程的第一個例子也不例外,就是編寫一個hello world模塊。

首先讓我們在電腦里編寫一段C語言代碼,hello.c。代碼如下:

//必要的頭文件
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
//模塊許可證聲明(必須)
MODULE_LICENSE("Dual BSD/GPL");
//模塊加載函數(必須)
static int hello_init(void)
{
    printk(KERN_ALERT "Hello World enter/n");
    return 0;
}
//模塊卸載函數(必須)
static void hello_exit(void)
{
    printk(KERN_ALERT "Hello World exit/n");
}
//模塊的注冊
module_init(hello_init);
module_exit(hello_exit);
//聲明模塊的作者(可選)
MODULE_AUTHOR("XXX");
//聲明模塊的描述(可選)
MODULE_DESCRIPTION("This is a simple example!/n");
//聲明模塊的別名(可選)
MODULE_ALIAS("A simplest example");

以上就是一個最簡單的內核模塊的例子程序。我們通過這個例子來分析一下內核模塊的特點。

1、在內核模塊的開始一部分,跟C語言的一般程序一樣,是模塊所需要的頭文件。

2、模塊許可證聲明,這部分是必須有的。模塊許可證(LICENSE)聲明描述內核模塊的許可權限,如果不聲明LICENSE,模塊被加載時,將收到內核被污染(kernel tainted)的警告。大多數情況下,內核模塊應遵守GPL兼容許可權。Linux2.6內核模塊最常見的是以MODULE_LICENSE("Dual BSD/GPL")語句聲明模塊采用BSD/GPL雙LICENSE。

3、模塊加載函數,這部分是必須的。模塊加載函數必須以“module_init(函數名)“的形式被指定。它返回整形值,若初始化成功,應返回0。在上面那個例子當中,hello_init()函數就是模塊加載函數需要執行的,主要是打印一條信息。

4、跟模塊加載函數相對應的就是模塊卸載函數,這部分也是必須的。模塊卸載函數在模塊卸載的時候執行,不返回任何值,必須以“module_exit(函數名)“的形式來指定。在上面的例子中,hello_exit()函數就是模塊卸載函數需要執行的,只要是打印了一條退出信息。

5、函數最后的一部分,是模塊聲明與描述部分。這部分可以有,也可以省略。在Linux內核模塊中,我們可以用MODULE_AUTHOR,MODULE_DESCRIPTION,MODULE_VERSION,MODULE_DEVICE_TABLE,MODULE_ALIAS分別聲明模塊的作者、描述、版本、設備表和別名。

注:

1、module_init()是驅動程序初始化的入口點。它就相當於c語言程序中的main()函數。對於內置的模塊,內核在引導時調用該引導點,對於可加載模塊則在模塊插入到內核時才調用。

2、模塊加載函數和模塊卸載函數中都用到了printk()函數,該函數是由內核定義的,功能與C庫中的printf()類似,它把要打印的信息輸出到終端或系統日志中。在本例中,我們是將初始化的打印信息輸出到日志中,我們在看它對應的輸出時,這時可以用dmesg命令來查看。

編寫完了.c文件,下面我們就要對其進行對應的操作,要把一個普通的.c文件變成我們所需要的內核文件。一般我們理解,應該是應用幾條Linux下的命令就可以搞定(如gcc,g++……),這里的理解是對的,我們就是需要幾個命令就OK。但是我們知道,編譯這個需要敲打的命令過於多,要輸入內核版本的號,路徑,和編寫模塊的路徑與信息。如果每次都輸入這么多,那肯定是太麻煩。這時我們就想到了Makefile文件,通過它來管理一個龐大的項目是再好不過的。下面我們就在剛才.c文件目錄下編寫一個Makefile文件。對應的代碼如下:

obj-m += hello.o
#generate the path
CURRENT_PATH:=$(shell pwd)
#the current kernel version number
LINUX_KERNEL:=$(shell uname -r)
#the absolute path
LINUX_KERNEL_PATH:=/usr/src/linux-headers-$(LINUX_KERNEL)
#complie object
all:
    make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
#clean
clean:
    make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean

首先第一句話就是指定要編譯的文件。

pwd是獲得當前的相對路徑,然后就是獲得當前的內核版本號,我們可以用uname -r命令,這樣我們就獲得了當前內核的絕對路徑。這樣做的一個好處,就是你可以在不同的內核版本中進行移植,而且可讀性也增強了。

對於其他的Makefile語法上的問題就不在這里介紹。不會的,可以看看相應的語法介紹。

有了Makefile文件后,我們就離成功不遠了。在.c文件的同一目錄下,執行make命令,系統會在當前目錄下生成好多個文件。其中就有與之相關的.o和.ko文件。hello.ko就是模塊目標文件。到此,模塊編譯好了。

模塊編譯好了,但是還不能為我們工作。下面就是將目標模塊插入到內核和從內核中刪除。這里需要用到兩個命令,insmod和rmmod 我們光看這兩個命令單詞就能猜出他們的意思。輸入命令:sudo insmod hello.ko(注意要用sudo),這時沒有任何提示,很多人會很奇怪,剛才不是說過,模塊加載后,程序中要對應輸出一條提示信息,怎么這里什么都沒有。大家不要急,再想一想剛才所用到的打印信息的函數printk(),它與我們平常C庫的printf()函數不一樣,不是運行在用戶界面上的,所以肯定不會在終端上顯示出信息。要看信息必須要進入到日志文件中。這時我們再輸入命令進到系統日志:dmesg,我們把界面拖到最后,會發現有一條信息,Hello World enter。哈哈,這正是我們所需要的,說明我們剛才編寫的模塊已經插入到內核當中了。接下來再試一試刪除命令,輸入命令:sudo rmmod hello.ko,這時跟剛才的插入命令一樣,沒有什么反應。再輸入命令打開系統日志,我們會發現在剛才 Hello World enter命令后面會有一個新的信息Hello World exit,這說明我們的模塊卸載成功。這樣我們就大功告成,慶祝一下吧。

除了加載和刪除模塊,我們也可以用命令 lsmod 來查看當前系統中加載的所有模塊及模塊間的依賴關系。如果剛才我們加載了hello模塊並沒有刪除,用這個命令,我們會在其中找到hello這一項,這樣也可以說明我們自己編寫的模塊加載成功。相應的,如果我們刪除了該模塊,用這個命令后,hello模塊不會出現。lsmod命令實際上讀取並分析/proc/modules文件。

使用modinfo<模塊名>命令可以獲得模塊的信息,包括模塊的作者,說明,參數……也就是我們剛才編寫模塊時可選的幾個部分。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM