Linux內核編程初體驗 —— hello world

我們實驗班的學習方向是Linux設備驅動編寫。Linux設備驅動會以內核模塊的形式出現,因此,學會編寫Linux內核模塊編程是學習Linux設備驅動的先決條件。

 

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

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

 

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

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

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

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

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文件。對應的代碼如下:

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

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<模塊名>命令可以獲得模塊的信息,包括模塊的作者,說明,參數……也就是我們剛纔編寫模塊時可選的幾個部分。

 

到此爲止,最基本的模塊編寫的入門就完了。這只是個基礎,希望我們大家都能練習一下。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章