Linux驅動設計—— 內核模塊(一)

 

Linux內核理論基礎html


 

組成Linux內核的5個子系統:進程調度(SCHED)/內存管理(MM)/虛擬文件系統(VFS)/網絡接口(NET)/進程間通訊(IPC)。node

進程調度(SCHED)linux

在設備驅動編程中,當請求的資源不能獲得知足時,驅動通常會調度其餘進程執行,並使本進程進入睡眠狀態,直到它請求的資源被釋
放,纔會被喚醒而進入就緒態。睡眠分紅可被打斷的睡眠和不可被打斷的睡眠,二者的區別在於可被打斷的睡眠在收到信號的時候會醒。shell

內存管理(MM)編程

內存管理的主要做用是控制多個進程安全地共享主內存區域。當CPU 提供內存管理單元(MMU)時,Linux 內存管理完成爲每一個進程進行虛擬內存到物理內存的轉換。數組

虛擬文件系統(VFS)安全

Linux 虛擬文件系統(VFS)隱藏各類了硬件的具體細節,爲全部的設備提供了統一的接口。它獨立於各個具體的文件系統,是對各類文件系統的一個抽象,它使用超級塊super block 存放文件系統相關信息,使用索引節點inode 存放文件的物理信息,使用目錄項dentry 存放文件的邏輯信息。服務器

網絡接口(NET)網絡

網絡接口提供了對各類網絡標準的存取和各類網絡硬件的支持。svn

在Linux 中網絡接口可分爲網絡協議和網絡驅動程序,網絡協議部分負責實現每一種可能的網絡傳輸協議,網絡設備驅動程序負責與硬件設備通訊,每一種可能的硬件設備都有相應的設備驅動程序。

進程間通訊(IPC)

進程通訊支持提供進程之間的通訊,Linux 支持進程間的多種通訊機制,包含信號量、共享內存、管道等,這些機制可協助多個進程、多資源的互斥訪問、進程間的同步和消息傳遞。

Linux 內核的5 個組成部分之間的依賴關係以下:
一、進程調度與內存管理之間的關係:這兩個子系統互相依賴。在多道程序環境下,程序要運行必須爲之建立進程,而建立進程的第一件事情,就是將程序和數據裝入內存。
二、進程間通訊與內存管理的關係:進程間通訊子系統要依賴內存管理支持共享內存通訊機制,這種機制容許兩個進程除了擁有本身的私有空間,還能夠存取共同的內存區域。
三、虛擬文件系統與網絡接口之間的關係:虛擬文件系統利用網絡接口支持網絡文件系統(NFS),也利用內存管理支持RAMDISK 設備。
四、內存管理與虛擬文件系統之間的關係:內存管理利用虛擬文件系統支持交換,交換進程(swapd)按期由調度程序調度,這也是內存管理依賴於進程調度的唯一緣由。當一個進程存取的內存映射被換出時,內存管理向文件系統發出請求,同時,掛起當前正在運行的進程。


 

Linux內核空間與用戶空間

現代CPU 內部每每實現了不一樣的操做模式(級別),不一樣的模式有不一樣的功能,高層程序每每不能訪問低級功能,而必須以某種方式切換到低級模式。

ARM 處理器分爲7 種工做模式:

! 用戶模式(usr):大多數的應用程序運行在用戶模式下,當處理器運行在用戶模式下時,某些被保護的系統資源是不能被訪問的。
! 快速中斷模式(fiq):用於高速數據傳輸或通道處理。
! 外部中斷模式(irq):用於通用的中斷處理。
! 管理模式(svc):操做系統使用的保護模式。
! 數據訪問終止模式(abt):當數據或指令預取終止時進入該模式,可用於虛擬存儲及存儲保護。
! 系統模式(sys):運行具備特權的操做系統任務。
! 未定義指令停止模式(und):當未定義的指令執行時進入該模式,可用於支持硬件協處理器的軟件仿真。

ARM Linux 的系統調用實現原理是採用swi 軟中斷從用戶態usr 模式陷入內核態svc 模式

X86 處理器包含4 個不一樣的特權級,稱爲Ring 0~Ring 3。Ring0 下,能夠執行特權級指令,對任何I/O 設備都有訪問權等,而Ring3 則被限制不少操做。

Linux 系統充分利用CPU 的這一硬件特性,但它只使用了兩級。在Linux 系統中,內核可進行任何操做,而應用程序則被禁止對硬件的直接訪問和對內存的未受權訪問。例如,若使用X86處理器,則用戶代碼運行在特權級3,而系統內核代碼則運行在特權級0內核空間用戶空間這兩個名詞被用來區分程序執行的這兩種不一樣狀態,它們使用不一樣的地址空間。Linux 只能經過系統調用硬件中斷完成從用戶空間到內核空間的控制轉移。

 


 

Linux下編碼風格:(與window作對比)

Windows 程序中:

#define PI 3.141 592 6 /*用大寫字母表明宏*/
int minValue, maxValue; /*變量:第一個單詞全寫,其後的單詞第一個字母小寫*/
void SendData(void); /*函數:全部單詞第一個字母都大寫定義*/

相對應的Linux程序:

#define PI 3.141 592 6
int min_value, max_value;
void send_data(void);

Linux 的代碼縮進使用「TAB」(8 個字符)。
Linux 的代碼括號「{」和「}」的使用原則以下。
(1)對於結構體、if/for/while/switch 語句,「{」不另起一行。

(2)若是 if、for 循環後只有 1 行,不要加「{」和「}」。

(3)if 和 else 混用的狀況下,else 語句不另起一行。

(4)對於函數,「{」另起一行。在switch/case 語句方面,Linux 建議switch 和case 對齊。

另外,請注意代碼中空格的應用,譬如「for!(i!=!0;! i!<!10;! i++)!{」語句中「!」都是空格。

 

GNU C對ANSI C的拓展 (詳見《Linux設備驅動開發詳解 第2版》P73)

1.零長度和變量長度數組

struct var_data {
int len;
char data[0];  //char data[0]僅僅意味着程序中經過var_data 結構體實例的data[index]成員能夠訪問len 以後的第index 個地址,它並無爲data[]數組分配內存,所以sizeof(struct var_data)=sizeof(int)。
};

struct var_data s;
...
for (i = 0; i < s.len; i++)
printf("%02x", s.data[i]);

 變量長度數組例子:

int main (int argc, char *argv[])
{
    int i, n = argc;
    double x[n]; 
    for (i = 0; i < n; i++)
    x[i] = i;
    return 0;
}

2.case 範圍

GNU C 支持case x…y 這樣的語法,區間[x,y]的數都會知足這個case 的條件。看到這個想起了曾經遇到的一段代碼就是這樣子,當時還以爲很奇怪。

switch (ch) {
case '0'... '9': c -= '0';
break;
case 'a'... 'f': c -= 'a' - 10;
break;
case 'A'... 'F': c -= 'A' - 10;
break;
}

3.語句表達式
GNU C 把包含在括號中的複合語句看作是一個表達式,稱爲語句表達式,它能夠出如今任何容許表達式的地方。咱們能夠在語句表達式中使用本來只能在複合語句中使用的循環、局部變量等。

//GNU C能夠這樣
#define min_t(type,x,y) \
({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })
int ia, ib, mini;
float fa, fb, minf;
mini = min_t(int, ia, ib);
minf = min_t(float, fa, fb);
//由於從新定義了_ _xx 和_ _y 這兩個局部變量,因此以上述方式定義的宏將不會有反作用。在標準C 中,對應的以下宏則會產生反作用:

//ANSI C只能這樣
#define min(x,y) ((x) < (y) ? (x) : (y))
//代碼min(++ia,++ib)會被展開爲((++ia) < (++ib) ? (++ia): (++ib)),傳入宏的「參數」被增長2 次。

4.typeof 關鍵字
typeof(x)語句能夠得到x 的類型。

#define min(x,y) ({ \
const typeof(x) _x = (x); \
const typeof(y) _y = (y); \
(void) (&_x == &_y); \
_x < _y ? _x : _y; })

//咱們不須要像min_t(type,x,y)這個宏那樣把type 傳入,由於經過typeof(x)、typeof(y)能夠得到type。代碼行(void) (&_x == &_y)的做用是檢查_x 和_y 的類型是否一致。

5.可變參數宏

標準C 就支持可變參數函數,意味着函數的參數是不固定的,例如printf()函數的原型爲:
int printf( const char *format [, argument]... );

而在 GNU C 中,宏也能夠接受可變數目的參數,例如:
#define pr_debug(fmt,arg...) \
    printk(fmt,##arg)
這裏arg 表示其他的參數,能夠是零個或多個,這些參數以及參數之間的逗號構成arg 的值,在宏擴展時替換arg,例以下列代碼:
  pr_debug("%s:%d",filename,line)
  會被擴展爲:
  printk("%s:%d", filename, line)
  使用「##」的緣由是處理arg 不表明任何參數的狀況,這時候,前面的逗號就變得多餘了。

使用「##」以後,GNU C 預處理器會丟棄前面的逗號,這樣,代碼:
  pr_debug("success!\n")
  會被正確地擴展爲:
  printk("success!\n")
  而不是:
  printk("success!\n",)
  這正是咱們但願看到的。

6.標號元素 

標準C 要求數組或結構體的初始化值必須以固定的順序出現,在GNU C 中,經過指定索引或結構體成員名,容許初始化值以任意順序出現。

指定數組索引的方法是在初始化值前添加「[INDEX] =」,固然也能夠用「[FIRST ... LAST] =」的形式指定一個範圍。例如,下面的代碼定義一個數組,並把其中的全部元素賦值爲0:
unsigned char data[MAX] = { [0 ... MAX-1] = 0 };

下面的代碼藉助結構體成員名初始化結構體:
struct file_operations ext2_file_operations = {
  llseek: generic_file_llseek,
  read: generic_file_read,
  write: generic_file_write,
  ioctl: ext2_ioctl,
  mmap: generic_file_mmap,
  open: generic_file_open,
  release: ext2_release_file,
  fsync: ext2_sync_file,
};

Linux 2.6 推薦相似的代碼應該儘可能採用標準C 的方式:
struct file_operations ext2_file_operations = {
  .llseek = generic_file_llseek,
  .read = generic_file_read,
  .write = generic_file_write,
  .aio_read = generic_file_aio_read,
  .aio_write = generic_file_aio_write,
  .ioctl = ext2_ioctl,
  .mmap = generic_file_mmap,
  .open = generic_file_open,
  .release = ext2_release_file,
  .fsync = ext2_sync_file,
  .readv = generic_file_readv,
  .writev = generic_file_writev,
  .sendfile = generic_file_sendfile,
};

 

7.當前函數名

GNU C 預約義了兩個標誌符保存當前函數的名字,_ _FUNCTION_ _保存函數在源碼中的名字,__PRETTY_FUNCTION_ _保存帶語言特點的名字。在C 函數中,這兩個名字是相同的。

  void example()
  {
    printf("This is function:%s", _ _FUNCTION_ _);
  }
代碼中的_ _FUNCTION_ _意味着字符串「example」。C99 已經支持_ _func_ _宏,所以建議在
Linux 編程中再也不使用_ _FUNCTION_ _,而轉而使用_ _func_ _:
  void example()
  {
    printf("This is function:%s", _ _func_ _);
  }

 

8.特殊屬性聲明

http://www.cnblogs.com/astwish/p/3460618.html

http://blog.chinaunix.net/uid-178707-id-2849461.html 每一個知識點都帶有實例

GNU C 容許聲明函數、變量和類型的特殊屬性,以便進行手工的代碼優化和定製代碼檢查的方法。要指定一個聲明的屬性,只須要在聲明後添加_ _attribute_ _ (( ATTRIBUTE ))。ATTRIBUTE 爲屬性說明,若是存在多個屬性,則以逗號分隔。GNU C 支持noreturn、format、section、aligned、packed 等十多個屬性。   

noreturn 屬性做用於函數,表示該函數從不返回。這會讓編譯器優化代碼,並消除沒必要要的
警告信息。例如:
# define ATTRIB_NORET _ _attribute_ _((noreturn)) ....
asmlinkage NORET_TYPE void do_exit(long error_code) ATTRIB_NORET;
format 屬性也用於函數,表示該函數使用printf、scanf 或strftime 風格的參數,指定format
屬性可讓編譯器根據格式串檢查參數類型。例如:
asmlinkage int printk(const char * fmt, ...) _ _attribute_ _ ((format (printf, 1, 2)));
上述代碼中的第1 個參數是格式串,從第2 個參數開始都會根據printf()函數的格式串規則檢
查參數。
unused 屬性做用於函數和變量,表示該函數或變量可能不會被用到,這個屬性能夠避免編譯
器產生警告信息。
aligned 屬性用於變量、結構體或聯合體,指定變量、結構體或聯合體的對界方式,以字節爲
單位,例如:
struct example_struct {
char a;
int b;
long c;
} _ _attribute_ _((aligned(4)));
表示該結構類型的變量以4 字節對界。
packed 屬性做用於變量和類型,用於變量或結構體成員時表示使用最小可能的對界,用於枚
舉、結構體或聯合體類型時表示該類型使用最小的內存。例如:

struct example_struct {
  char a;
  int b;
  long c _ _attribute_ _((packed));
};

 

9.內建函數

 


 

Linux內核模塊

Linux內核模塊的特色

一、模塊自己不被編譯入內核映像,從而控制了內核的大小。

二、模塊一旦被加載,它就和內核中的其餘部分徹底同樣。

一個最簡單的Linux 內核模塊 「Hello World」

1 #include <linux/init.h>
2 #include <linux/module.h>
3
4 static int hello_init(void)
5 {
6   printk(KERN_INFO " Hello World enter\n");
7   return 0;
8 }
9
10 static void hello_exit(void)
11 {
12   printk(KERN_INFO " Hello World exit\n ");
13 }
14
15 module_init(hello_init);
16 module_exit(hello_exit);
17
18 MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
19 MODULE_LICENSE("Dual BSD/GPL");
20 MODULE_DESCRIPTION("A simple Hello World Module");
21 MODULE_ALIAS("a simplest module");

在Linux 中,使用lsmod 命令能夠得到系統中加載了的全部模塊以及模塊間的依賴關係,lsmod 命令實際上讀取並分析「/proc/modules」文件。/proc 文件系統是動態的,因此驅動程序模塊能夠在任什麼時候候添加或刪除其中的文件項。

lihacker@lihacker-laptop:~/$ cat /proc/modules
hello 9472 0 - Live 0xf953b000  # 模塊名 佔用空間bytes 模塊加載計數 Live表示模塊可用 0xf953b000是模塊的起始地址
nls_iso8859_1 12032 1 - Live 0xf950c000
nls_cp437 13696 1 - Live 0xf9561000
vfat 18816 1 - Live 0xf94f3000

 

內核中已加載模塊的信息也存在於/sys/module 目錄下,加載hello.ko 後,內核中將包含/sys/module/hello 目錄,該目錄下又包含一個refcnt 文件和一個sections 目錄,在/sys/module/hello目錄下運行「tree –a」獲得以下目錄樹:

(不必定包含全部的目錄)

holders  持有人,是寫本模塊的人。可是目錄爲空。

initstate  記錄模塊活動

notes   暫且沒有查到,好像是日記,有個隱藏文件,可能就是記錄本模塊的信息 

parameters  使用的變量

全部內聯模塊的參數也能夠由 "<module_name>.<param_name>=<value>" 的形式寫在內核啓動參數上,如啓動內核時加上參數 "printk.time=1" 與 向 "/sys/module/printk/parameters/time" 寫入1的效果相同。

refcnt   模塊的加載計數

section 段信息

srcversion  模塊版本號 像模塊的ID同樣

lihacker@lihacker-laptop:/sys/module/hello$ tree -a
.
|-- holders
|-- initstate
|-- notes
| '-- .note.gnu.build-id
|-- refcnt
|-- sections
| |-- .bss
| |-- .data
| |-- .gnu.linkonce.this_module
| |-- .note.gnu.build-id
| |-- .rodata.str1.1
| |-- .strtab
| |-- .symtab
| '-- .text
'-- srcversion
3 directories, 12 files

modprobe 命令比insmod 命令要強大,它在加載某模塊時,會同時加載該模塊所依賴的其餘模塊。使用modprobe 命令加載的模塊若以「modprobe -r filename」的方式卸載將同時卸載其依賴的模塊
使用modinfo <模塊名>命令能夠得到模塊的信息,包括模塊做者、模塊的說明、模塊所支持的參數以及vermagic:

模塊信息在代碼中指定參考http://www.cnblogs.com/kwseeker-bolgs/p/4343392.html

lihacker@lihacker-laptop: ~ /develop/svn/ldd6410-read-only/training/kernel/drivers/hello$ modinfo hello.ko
filename: hello.ko
alias: a simplest module      
description: A simple Hello World Module
license: Dual BSD/GPL
author: Barry Song <21cnbao@gmail.com>
srcversion: 3FE9B0FBAFDD565399B9C05
depends:
vermagic: 2.6.28-11-generic SMP mod_unload modversions 586

  

Linux內核模塊程序結構

(1)模塊加載函數(通常須要)

Linux 內核模塊加載函數通常以_ _init 標識聲明

static int _ _init initialization_function(void) //它返回整型值,若初始化成功,應返回0。而在初始化失敗時,應該返回錯誤編碼。在Linux 內核裏,錯誤編碼是一個負值,在<linux/errno.h>中定義,包含-ENODEV、-ENOMEM 之類的符號值。用戶程序能夠利用perror 等方法把它們轉換成有意義的錯誤信息字符串。
{
    /* 初始化代碼 */
}
module_init(initialization_function);

在Linux 中,全部標識爲_ _init 的函數在鏈接的時候都放在.init.text 這個區段內,此外,全部的_ _init 函數在區段.initcall.init 中還保存了一份函數指針,在初始化時內核會經過這些函數指針調用這些_ _init 函數,並在初始化完成後,釋放init 區段(包括.init.text、.initcall.init 等)。

(2)模塊卸載函數(通常須要)

Linux 內核模塊加載函數通常以_ _exit 標識聲明。

static void _ _exit cleanup_function(void)
{
     /* 釋放代碼 */
}
module_exit(cleanup_function);

模塊卸載函數要完成與模塊加載函數相反的功能

! 若模塊加載函數註冊了XXX,則模塊卸載函數應該註銷XXX。
! 若模塊加載函數動態申請了內存,則模塊卸載函數應釋放該內存。
! 若模塊加載函數申請了硬件資源(中斷、DMA 通道、I/O 端口和I/O 內存等)的佔用,
則模塊卸載函數應釋放這些硬件資源。
! 若模塊加載函數開啓了硬件,則卸載函數中通常要關閉之。

和_ _init 同樣,_ _exit 也可使對應函數在運行完成後自動回收內存。實際上,_ _init 和_ _exit
都是宏,其定義分別爲:

#define _ _init _ _attribute_ _ ((_ _section_ _ (".init.text"))) 

#ifdef MODULE
#define _ _exit _ _attribute_ _ ((_ _section_ _(".exit.text")))
#else
#define _ _exit _ _attribute_used_ _attribute_ _ ((_ _section_ _(".exit.text")))
#endif

數據也能夠被定義爲_ _initdata 和_ _exitdata,這兩個宏分別爲:

#define _ _initdata _ _attribute_ _ ((_ _section_ _ (".init.data")))

#define _ _exitdata _ _attribute_ _ ((_ _section_ _(".exit.data")))

  

(3)模塊許可證聲明(必須)

在Linux 2.6 內核中,可接受的LICENSE 包括「GPL」、「GPL v2」、「GPL and additional rights」、「Dual BSD/GPL」、「Dual MPL/GPL」和「Proprietary」。大多數狀況下,內核模塊應遵循GPL 兼允許可權。Linux 2.6 內核模塊最多見的是以MODULE_LICENSE( "Dual BSD/GPL" )語句聲明模塊採用BSD/GPL 雙LICENSE。

(4)模塊參數(可選)

模塊參數是模塊被加載的時候能夠被傳遞給它的值,它自己對應模塊內部的全局變量

能夠用「module_param(參數名,參數類型,參數讀/寫權限)」爲模塊定義一個參數,例以下列代碼定義了1 個整型參數和1 個字符指針參數:

static char *book_name = " dissecting Linux Device Driver ";
static int num = 4 000;
module_param(num, int, S_IRUGO);
module_param(book_name, charp, S_IRUGO);

在裝載內核模塊時,用戶能夠向模塊傳遞參數,形式爲「insmode(或modprobe)模塊名 參數名=參數值」,若是不傳遞,參數將使用模塊內定義的缺省值。參數類型能夠是byte、short、ushort、int、uint、long、ulong、charp(字符指針)、bool 或invbool(布爾的反),在模塊被編譯時會將module_param 中聲明的類型與變量定義的類型進行比較,判斷是否一致。

perm參數(參數權限)的做用是什麼?
最後的 module_param 字段是一個權限值; 你應當使用 中定義的值. 這個值控制誰能夠存取這些模塊參數在 sysfs 中的表示.若是 perm 被設爲 0, 就根本沒有 sysfs 項. 不然, 它出如今 /sys/module下面, 帶有給定的權限. 使用 S_IRUGO 做爲參數能夠被全部人讀取, 可是不能改變; S_IRUGO|S_IWUSR 容許 root 來改變參數. 注意, 若是一個參數被 sysfs 修改, 你的模塊看到的參數值也改變了, 可是你的模塊沒有任何其餘的通知. 你應當不要使模塊參數可寫, 除非你準備好檢測這個改變而且於是做出反應.
perm參數的做用是什麼?
最後的 module_param 字段是一個權限值,表示此參數在sysfs文件系統中所對應的文件節點的屬性。你應當使用 中定義的值. 這個值控制誰能夠存取這些模塊參數在 sysfs 中的表示.當perm爲0時,表示此參數不存在 sysfs文件系統下對應的文件節點。 不然, 模塊被加載後,在/sys/module/ 目錄下將出現以此模塊名命名的目錄, 帶有給定的權限.。
權限在include/linux/stat.h中有定義
好比:
#define S_IRWXU 00700
#define S_IRUSR 00400
#define S_IWUSR 00200
#define S_IXUSR 00100
#define S_IRWXG 00070
#define S_IRGRP 00040
#define S_IWGRP 00020
#define S_IXGRP 00010
#define S_IRWXO 00007
#define S_IROTH 00004
#define S_IWOTH 00002
#define S_IXOTH 00001
使用 S_IRUGO 做爲參數能夠被全部人讀取, 可是不能改變; S_IRUGO|S_IWUSR 容許 root 來改變參數. 注意, 若是一個參數被 sysfs 修改, 你的模塊看到的參數值也改變了, 可是你的模塊沒有任何其餘的通知. 你應當不要使模塊參數可寫, 除非你準備好檢測這個改變而且於是做出反應.

經過宏MODULE_PARM_DESC()對參數進行說明:
static unsigned short size = 1;
module_param(size, ushort, 0644);
MODULE_PARM_DESC(size, 「The size in inches of the fishing pole」
「connected to this computer.」 );

帶參數的內核模塊

#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");

static char *book_name = "dissecting Linux Device Driver";
static int num = 4 000;

static int book_init(void)
{
printk(KERN_INFO " book name:%s\n",book_name);
printk(KERN_INFO " book num:%d\n",num);
return 0;
}
static void book_exit(void)
{
printk(KERN_INFO " Book module exit\n ");
}
module_init(book_init);
module_exit(book_exit);
module_param(num, int, S_IRUGO);
module_param(book_name, charp, S_IRUGO);

MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
MODULE_DESCRIPTION("A simple Module for testing module params");
MODULE_VERSION("V1.0");

對上述模塊運行「insmod book.ko」命令加載,相應輸出都爲模塊內的默認值,經過查看
/var/log/messages」日誌文件能夠看到內核的輸出:
[root@localhost driver_study]# tail -n 2 /var/log/messages
Jul 2 01:03:10 localhost kernel: <6> book name:dissecting Linux Device Driver
Jul 2 01:03:10 localhost kernel: book num:4 000
當用戶運行「insmod book.ko book_name='GoodBook' num=5 000」命令時,輸出的是用戶傳遞
的參數:
[root@localhost driver_study]# tail -n 2 /var/log/messages
Jul 2 01:06:21 localhost kernel: <6> book name:GoodBook
Jul 2 01:06:21 localhost kernel: book num:5 000


(5)模塊導出符號(可選)
內核模塊能夠導出符號(symbol,對應於函數或變量),這樣其餘模塊可使用本模塊中的變量或函數。

Linux 2.6 的「/proc/kallsyms」文件對應着內核符號表,它記錄了符號以及符號所在的內存地址。

模塊可使用以下宏導出符號到內核符號表:

EXPORT_SYMBOL(符號名);
EXPORT_SYMBOL_GPL(符號名);

導出的符號將能夠被其餘模塊使用,使用前聲明一下便可。EXPORT_SYMBOL_GPL()只適用於包含GPL 許可權的模塊。


(6)模塊做者等信息聲明(可選)

在Linux 內核模塊中,咱們能夠用MODULE_AUTHOR、MODULE_DESCRIPTION、MODULE_VERSION、MODULE_DEVICE_TABLE、MODULE_ALIAS 分別聲明模塊的做者、描述、版本、設備表和別名。

MODULE_AUTHOR(author);
MODULE_DESCRIPTION(description);
MODULE_VERSION(version_string);
MODULE_DEVICE_TABLE(table_info);
MODULE_ALIAS(alternate_name);

對於USB、PCI 等設備驅動,一般會建立一個MODULE_DEVICE_TABLE,代表該驅動模塊所支持的設備。

 

模塊的使用計數

Linux 2.6 內核提供了模塊計數管理接口try_module_get(&module)和module_put (&module),從而取代Linux 2.4 內核中的模塊使用計數管理宏。模塊的使用計數通常沒必要由模塊自身管理,並且模塊 計數管理還考慮了 SMP 與 PREEMPT 機制的影響。

int try_module_get(struct module *module);
該函數用於增長模塊使用計數;若返回爲0,表示調用失敗,但願使用的模塊沒有被加載或正在 被卸載中。
void module_put(struct module *module);
該函數用於減小模塊使用計數。

 

模塊的編譯模板

KVERS = $(shell uname -r)
# Kernel modules
obj-m += hello.o
# Specify flags for the module compilation.
#EXTRA_CFLAGS=-g -O0
build: kernel_modules
kernel_modules:
make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modules
clean:
make -C /lib/modules/$(KVERS)/build M=$(CURDIR) clean

若是一個模塊包括多個.c 文件(如file1.c、file2.c),則應該以以下方式編寫Makefile:

obj-m := modulename.o
modulename-objs := file1.o file2.o

 


Linux文件系統與設備文件系統

 

Linux文件操做

參考:http://www.cnblogs.com/kwseeker-bolgs/p/4373131.html

 

Linux文件系統

1./bin 包含基本命令,這個目錄中的文件都是可執行的。
2./sbin 包含系統命令,如modprobe、hwclock、ifconfig 等,大可能是涉及系統管理的命令,這個目錄中的文件都是可執行的。
3./dev 設備文件存儲目錄,應用程序經過對這些文件的讀寫和控制就能夠訪問實際的設備。
4./etc 系統配置文件的所在地,一些服務器的配置文件也在這裏,如用戶帳號及密碼配置文件。busybox 的啓動腳本也存放在該目錄。
5./lib 系統庫文件存放目錄。
6./mnt 通常是用於存放掛載儲存設備的掛載目錄的,好比有cdrom 等目錄。能夠參看/etc/fstab 的定義。
7./opt opt 是「可選」的意思,有些軟件包會被安裝在這裏,例如,在LDD6410 的文件系統中,Qt/Embedded 就存放在該目錄。

8./proc 操做系統運行時,進程及內核信息(好比CPU、硬盤分區、內存信息等)存放在這裏。/proc目錄爲僞文件系統proc 的掛載目錄,proc 並非真正的文件系統,它存在於內存之中。
9./tmp 有時用戶運行程序的時候,會產生臨時文件,/tmp 就用來存放臨時文件的。
10./usr 這個是系統存放程序的目錄,好比用戶命令、用戶庫等。
11./var var 表示的是變化的意思,這個目錄的內容常常變更,如/var 的/var/log 目錄被用來存放系統日誌。
12./sys Linux 2.6 內核所支持的sysfs 文件系統被映射在此目錄。Linux 設備驅動模型中的總線、驅動和設備均可以在sysfs 文件系統中找到對應的節點。當內核檢測到在系統中出現了新設備後,內核會在sysfs 文件系統中爲該新設備生成一項新的記錄。

 

Linux文件系統與設備驅動

磁盤文件(存放於Ramdisk、Flash、ROM、SD 卡、U盤等文件系統中的文件)

應用程序和VFS 之間的接口是系統調用

VFS 與磁盤文件系統以及普通設備之間的接口是file_operations 結構體成員函數,這個結構體包含對文件進行打開、關閉、讀寫、控制的一系列成員函數。

在設備驅動程序的設計中,通常而言,會關心file 和inode 這兩個結構體。

參考:http://www.cnblogs.com/kwseeker-bolgs/p/4363358.html

相關文章
相關標籤/搜索