linux device driver3 讀書筆記(一)

一:機制與策略(轉)html

  http://www.51hei.com/bbs/dpj-29441-1.htmllinux

  機制mechanism,策略policy。若是你看過《linux device drivers》,裏面給出了大概的介紹。機制提供了幹什麼(do what),策略提供如何作(how to do)。驅動程序完成機制的功能,把策略的實現留給用戶的應用程序。編程

 
       一般在機制中,驅動程序要完成打開,關閉,讀寫,控制等功能。這些都是設備使用時最基本的操做。而策略中就要實現一些高級的數據處理或界面功能。經過例子來講明會更好些。
 
       以RTC(實時時鐘設備)爲例。假設RTC共有8個REG,分別是2個控制寄存器、年寄存器、月寄存器、日寄存器、時寄存器、分寄存器、秒寄存器。RTC的啓動和關閉分別經過設定控制寄存器的某一個位來完成。
 
       1.    驅動程序提供open,release,write,read函數。Open和release很少說了,具體介紹一下read和write的功能。Read中經過IO端口操做能夠讀入這8個寄存器的值,Write中經過IO端口操做能夠寫入這8個寄存器的值。僅僅這樣驅動程序已經爲應用程序提供了全部完成對RTC設備訪問的接口。這樣或許對應用程序操做RTC不是很方便,所以在發佈驅動程序的時候,咱們提供了針對RTC的用戶函數庫。好比setDate,setTime等,這些函數無非就是把驅動程序中的read和write組合調用了一下。可是採用這種策略,用戶寫應用程序時就會更方便,對底層的細節就沒必要知道的太多。
 
       2.    另外一個能夠選擇的機制就是驅動程序中引入ioctl函數,驅動程序把setDate,setTime操做的功能在ioctl函數中實現。Ioctl經過IO端口操做訪問8個寄存器就能夠完成。未來驅動程序發佈時,不用提供用戶函數庫。只要告訴開發人員IOCTL函數的操做碼便可,使用不一樣的ioctl調用就能夠完成setDate,setTime功能。
 
      經過上面兩種狀況能夠看出,機制不一樣提供的策略選擇不一樣,機制能夠把策略中的一些功能集成。可是如何選擇正確的機制和策略組合呢?
 
      針對上面2中機制提供,分析各自優缺點:
      第一種方案中,因爲沒有提供Ioctl函數,驅動程序必然會佔用內存小。並且對於RTC設備,咱們不會常常去setDate,setTime,所以這種小几率使用的函數放到用戶庫中實現更好。
      第二中方案,不用提供用戶函數庫,發佈方便。可是ioctl函數會常駐內存中,儘管他可能一直得不到調用!這中方式更符合面向對象的編程思想。

 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------數據結構

二:用戶空間和內核空間併發

一個模塊在內核空間運行, 而應用程序在用戶空間運行. 這個概念是操做系統理論的基礎,函數

驅動模塊試涉及到內核態以及用戶態,當應用程序發出一個systerm call或者被硬件的中斷掛起,內核在上下文中執行systerm call(系統調用),內核和應用程序的memory map也不一樣,因此this

內核的內存空間以及應用程序的內存空間不能互相訪問,要用到copy_to_user 以及copy_from_user函數;spa

內核程序與應用程序最大的不一樣是內核是併發的,而應用程序是按順序執行的;因此應用程序不用擔憂上線文切換,而內核必需要作好上下文切換;操作系統

2.6內核之後,內核是搶佔式的,這必需要求內核代碼是可重入的code

內核的棧是很是小的,內核共用4k的棧,因此當須要大的結構體時,是使用堆內存;kzalloc

驅動模塊的代碼中的函數以及數據結構跟內核的版本緊密相連,因此編譯模塊的時候要選用正確的內核版本,安裝模塊的時候也要選擇正確的內核版本;

若是

 

2.6 預備知識

#include <linux/module.h>      包含大量的模塊加載卸載的頭文件 如module_init 、module_exit等函數
#include <linux/init.h>        指定清理的初始化函數,init的初始化函數

MODULE_LICENSE("GPL");      內核認識的特定許可有, "GPL"( 適用 GNU 通用公共許可的任何版本 ),

 

MODULE_AUTHOR ( 聲明誰編寫了模塊 ),

MODULE_DESCRIPION( 一我的可讀的關於模塊作什麼的聲明 ),

MODULE_VERSION ( 一個代碼修訂版本號

MODULE_ALIAS ( 模塊爲人所知的另外一個名子 )

以及 MODULE_DEVICE_TABLE ( 來告知用戶空間, 模塊支持那些設備 ).

 

 

2.7 初始化中的錯誤處理

這點很重要,好比你在insmodu 一個模塊的時候加載失敗,若是這個驅動模塊的代碼沒有進行相關錯誤處理,在加載會出現更多問題

錯誤恢復是最好使用goto語句;

 

 1 int __init my_init_function(void)  2 {  3 int err;  4 err = register_this(ptr1, "skull"); /* registration takes a pointer and a name */
 5 if (err)  6 goto fail_this;  7 err = register_that(ptr2, "skull");  8 if (err)  9 goto fail_that; 10 err = register_those(ptr3, "skull"); 11 if (err) 12 goto fail_those; 13 return 0; /* success */
14 fail_those: 15 unregister_that(ptr2, "skull"); 16 fail_that: 17 unregister_this(ptr1, "skull"); 18 fail_this: 19 return err; /* propagate the error */
20 }

 

或者在失敗的時候直接執行你的清理函數便可,可是這樣須要更多的上下文切換,消耗更過的內存空間,寄存器等;或者能夠下下面一個函數;

檢查每種操做的狀態,在清理,可是這樣作的壞處是須要定義不少歌變量,仍是用goto比較好;

 1 struct something *item1;  2 struct somethingelse *item2;  3 int stuff_ok;  4 void my_cleanup(void)  5 {  6 if (item1)  7 release_thing(item1);  8 if (item2)  9 release_thing2(item2); 10 if (stuff_ok) 11 unregister_stuff(); 12 return; 13 }
 1 int __init my_init(void)  2 {  3 int err = -ENOMEM;  4 item1 = allocate_thing(arguments);  5 item2 = allocate_thing2(arguments2);  6 if (!item2 || !item2)  7 goto fail;  8 err = register_stuff(item1, item2);  9 if (!err) 10 stuff_ok = 1; 11 else
12 goto fail; 13 return 0; /* success */
14 fail: 15 my_cleanup(); 16 return err; 17 }
相關文章
相關標籤/搜索