Linux驅動--(三)簡單的內核模塊驅動程序-下

(三)簡單的內核模塊驅動程序-下

一、概述

  • 上篇中我們講述了,簡單的內核模塊驅動程序。但是那並不是內核模塊的一般形式。

    不知你是否嘗試過將其加載到到內核中。如果你加載了,應該會看到以下輸出:

    # hello:module license 'unspecified' taints kernel
    # Disabling lock debugging due to kernel taint
    

    這兩句話的大意是:因爲加載了hello模塊而導致內核被污染,並且因此禁止了鎖的調試功能。

    你肯定很奇怪,這是爲什麼呢?其實呢,這是因爲GNU的規定,因爲Linux是開源項目,所以其要求任何組織或者個人在免費得到源碼並對其進行修改和再發布的同時,必須將修飾後的源碼發佈。這就是多爲的GPL許可證協議。

    我們只需要在代碼中加上如下一段代碼,就可以了。

    MODULE_LICENSE("GPL");
    

    MODULE_LICENSE是一個宏,裏面的參數是一個字符串,代表相應的許可證協議。可以是:GPL、GPL v2 、Dual MPL/GPL等其他不同的協議。我們可以在源碼樹目錄:include/linux/module.h裏面查看到。

    那麼這個東西的具體原理是什麼呢?原來,這些宏是會生成一些模塊信息,放在ELF文件中的一個特殊字段中,模塊在加載時會將該信息複製到內存中。內核運行時回去檢查該信息,如果沒有該信息,那麼內核中的很多功能我們是不能調用,比如說一些內核的API函數。

    好啦,現在你明白爲什麼要加這句話了吧。其實呢,內核彙總還有很多其宏,在我們編寫驅動時會經常用到。

    MODULE_AUTHOR("...");	//描述模塊的作者信息
    MODULE_DESCRIPTION();	//用於描繪模塊的詳細信息
    MODULE_ALIAS();	//可以給用戶空間使用的一個別名
    

    模塊的初始化以及模塊清除函數的名字都是固定的。但是GNU爲我們提供了一些函數別名機制。

    module_init(hello_init);
    module_exit(hello_exit);
    

    module_init()和module_exit()是兩個宏,分別用於指定init_module函數的別名是hello_int,cleanup_module函數的別名是hello_exit。

    好啦,這裏我們函數可以任意指定函數的別名啦,但是這樣又引入了一個新的問題。就是如果我們任意指定的一個函數與內核中的某個函數重名了該怎麼辦呢?因爲C原因沒有類似C++的函數命名空間以及重載的概念。而該模塊最終有時要加載進入內核成爲內核的一部分。別忘啦,我們C語言是由static關鍵字的,用它來修飾每一個函數不就是把函數的作用域限定到本文件內了嗎?所以呀,後面我們編寫內核模塊的時候,每個函數都最好加上static修飾。

  • 我們都知道,Linux在內存的利用上面可以說是教科書級別的了。衆多Linux大神在設計Linux時,是絕對不會浪費一丁點內存的。這當然值得我們在以後的編程中去學習。所以我們看看我們前面寫的程序是否有什麼問題呢?

    我們很容易發現,在原來的代碼中,模塊的初始化函數只會被調用一次,後面就不會再被調用。所以它佔用的內存應給被及時給釋放掉。這裏我們選擇在該函數前面加__init可以達到我們的目的。那麼__init由什麼功能呢?原來,該標記會會把標記的函數放到ELF文件的特定代碼段,在模塊加載這些段時會單獨分配內存,這些函數調用成功後,模塊的加載程序就會釋放這部分內存空間。那麼對於模塊清除函數,我們也有同樣的標記__exit用於修飾清除函數,和__init作用很類似,但是是用於模塊的卸載的。如果模塊是不允許卸載,那麼這段代碼完全就不用加載。

  • 好啦,有了以上的補充。我們來完善一下上次的程序:

/*******************hello********************/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

static int __init hello_init(void)
{
    printk("init module:hello world!\n ");
    return 0;
}

static void __exit hello_exit(void)
{
    printk("exit module:hello_exit!\n");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Fan <[email protected]>");
MODULE_DESCRIPTION("A hello module");
MODULE_ALIAS("printf_hello");
  • 好啦,這就是內核驅動模塊最小的一個模型啦。可以看看,是不是麻雀雖小,五臟俱全吶。

    再多說一句,我們對模塊的加載也可以使用別名,命令如下:

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