最近這一段時間,把之前學的linux基礎的東西撿了回來,正式開始接觸驅動了。接觸後發現底層的東西還是相當的繁瑣,核心裡的巨集定義數不勝數,結構體,指標更是撲面而來。驅動主要分為三部分,分別是字元裝置、塊裝置以及網路介面裝置。這裡先總結下簡單字元裝置驅動相關的東西。
參考資料1個人覺得不是很適合拿來上手,雖然講的點多,但是卻不細緻。而2講的點少,但比較細也比較淺顯。
字元裝置驅動:
字元裝置理解起來可以和塊裝置進行區分理解。
字元=只能乙個乙個位元組讀寫
塊=可以從任意位置讀取一定長度資料的裝置,不必按照先後順序。
如果對應上實物的話:字元裝置有:滑鼠、鍵盤、串列埠、控制台···
塊裝置有:sd卡、硬碟、磁碟、u盤···
linux具體是怎樣去操作裝置驅動的?
在linux系統中,每個字元裝置或者塊裝置都在 /dev目錄下有對應乙個裝置檔案。linux對裝置進行操控,本質上就是通過這些檔案來操作的。這樣相當於有了一套標準,程式設計師便可以撇開裝置的差異化從而按照標準進行程式設計。
出現以下條目:
crw-rw----+ 1 root root 14, 12 12-21 22:56 adsp
第乙個字元c 就表示char 字元型裝置 ,b則表示block 塊裝置。而5、6欄位分別表示主裝置號和次裝置號。
為什麼需要主裝置號和次裝置號呢?
主裝置號用來區分不同型別的裝置,如usb裝置和串列埠裝置。次裝置號則是用來區分某一型別裝置中不同的子裝置。如串列埠裝置不止一種,所以通過此裝置號進行區分。
主裝置號和次裝置號的表示(具體**實現)
linux中用dev_t 型別來表示裝置號。其實它本質上就是乙個無符號長整型,
typedef u_long dev_t
u_long 在32位機裡為4個位元組 ,在64位里為8位元組。由於自己學的是arm,以32位機為例。其中高12位表示主裝置號,低20位表示次裝置號。
裝置號的獲得與申請方式:
主要有2種,第一種為靜態申請,第二種自然就是動態申請。採用的方法不同,前期的基礎工作也就不同。
先說說靜態申請:靜態申請毫無疑問需要程式設計師自己給分配乙個裝置號,那麼怎樣判斷乙個裝置號是否可用呢?或者說當前系統裡沒有被其他裝置占用呢?
方法:可以讀取 /proc/devices 檔案來獲得裝置號。
指令如下:cat /proc/devices
知道當前系統占用的裝置號後,比如要需要設定的裝置號為200,那麼怎麼構建該裝置號呢?
linux系統採用mkdev來實現,其中
dev_t devno=mkdev(ma,mi);其中ma為主裝置號,mi為次裝置號。
構建完裝置號之後,還需要進行申請,靜態申請的方法為:
int register_chrdev_region(dev_t from, unsigned count , const char *name); 標頭檔案:
from 是要分配裝置號範圍起始值,count表示需要申請裝置號的個數。 name則是裝置名稱,注意不能超過64位元組。
動態分配:靜態分配由於人為因素,很可能導致衝突,所以linux自己給自動分配乙個未使用的裝置號則更加有利。
動態申請不需要自己構建裝置號,呼叫函式:
int alloc_chrdev_region(&dev_t *dev,unsigned baseminor,unsigned count, const char *name)
成功後返回的裝置號儲存在dev指向的dev_t 型別的變數裡。baseminor 為次裝置號的起始號,count為子裝置數 name為裝置名稱。
申請完後,要將字元裝置註冊到系統中,才能使用。
cdev 用來描述字元裝置。
struct cdev {
struct kobject kobj;
struct module *owner;/*指向包含該結構的模組的指標,用於引用計數*/
const struct file_operations *ops;/*指向字元裝置操作函式集的指標*/
struct list_head list; /*該結構將使用該驅動的字元裝置連成乙個鍊錶*/
dev_t dev; /*該字元裝置的起始裝置號*/
unsigned int count;/*使用該字元裝置驅動的裝置數量*/
kobj結構用於核心管理字元裝置,驅動開發人員一般不使用。
ops是指向file_operations 操作函式結構體指標。
list為雙向鍊錶,用於將其他結構體連線成乙個雙向鍊錶,其連線到inode結構體i_devices成員。而i_devices也是乙個list_head結構。這樣使得cdev結構與inode結點組成乙個雙向鍊錶。
檔案系統中對字元裝置檔案的訪問
對於乙個字元裝置檔案, 其inode->i_cdev 指向字元驅動物件cdev, 如果i_cdev為 null ,則說明該裝置檔案沒有被開啟.
由於多個裝置可以共用同乙個驅動程式.所以,通過字元裝置的inode 中的i_devices 和 cdev中的list組成乙個雙向鍊錶。inode表示/dev下的裝置檔案。每乙個字元裝置在/dev下都有乙個裝置檔案,開啟裝置檔案就相當於開啟相應的字元裝置。例如應用程式開啟裝置檔案a,那麼系統就會產生乙個inode結點,這樣可以通過inode結點的i_cdev欄位找到cdev字元結構體。
首先,系統呼叫open開啟乙個字元裝置的時候, 通過一系列呼叫,最終會執行到 chrdev_open.
(最終是通過呼叫到def_chr_fops中的.open, 而def_chr_fops.open = chrdev_open. 這一系列的呼叫過程,本文暫不討論)
int chrdev_open(struct inode * inode, struct file * filp)
chrdev_open()所做的事情可以概括如下:
1. 根據裝置號(inode->i_rdev), 在字元裝置驅動模型中查詢對應的驅動程式, 這通過kobj_lookup() 來實現, kobj_lookup()會返回對應驅動程式cdev的kobject.
2. 設定inode->i_cdev , 指向找到的cdev.
3. 將inode新增到cdev->list的鍊錶中.
4. 使用cdev的ops 設定file物件的f_op
5. 如果ops中定義了open方法,則呼叫該open方法
6. 返回.
執行完 chrdev_open()之後,file物件的f_op指向cdev的ops,因而之後對裝置進行的read, write等操作,就會執行cdev的相應操作.
實踐測試驅動開發
作為乙個有理想 有追求的程式設計師,你成天被各種名詞包圍著,你對其中乙個叫做敏捷的東西特別感興趣,因為它特別強調人的作用,這聽著都讓做程式設計師的你感到舒服。為了讓自己早日敏捷起來,你從眾多的敏捷實踐中選擇了乙個叫做測試驅動開發 test driven development,tdd 的作為你的起始...
Android測試驅動開發實踐
在android應用開發中,相信很少有人在堅持先由設計人員做完整的概要設計 詳細設計,然後交給程式設計師進行編碼實現了。通常是在有乙個大體框架的情況下,就開始進行具體編碼開發了。在這種情形下,開發速度可以有很大的提高,但是最終 質量卻不可避免的降低了。如何能既保持開發速度,同時又能保證開發質量呢?相...
測試開發驅動實踐
最近,測試驅動的概念慢慢被大家接受,kent back的書 測試驅動開發 中文版,也有中國電力出版社出版了。從2002年就開始,我使用測試驅動的方式開發,這兩年多裡,幾乎我所有的 都是基於測試驅動的方式進行開發。使用這種方式進行開發兩年,自然也有一些經驗。我這兩天看了kent back的 測試驅動開...