字元裝置驅動程式(一)
概念
linux系統從千千萬萬裝置中提取它們的共性,将這些裝置分成3大類:字元裝置,塊裝置,網絡裝置。
字元裝置是前面提到的這三類裝置中最常見的裝置,比如生活中大家常見的鍵盤、滑鼠、觸摸屏等都屬于字元裝置。是以掌握字元裝置驅動架構是每個驅動程式員所必須的。
應用程式與核心、驅動的關系
u-boot 啟動核心
核心 啟動應用程式
應用 讀寫檔案、擷取鍵值
用點燈程式為例
應用程式,直接調用open read write 來調用驅動中的led_open、led_read、led_write
字元驅動架構

1.首選上層應用程式調用open函數打開/dev目錄下的一個檔案 xxx(這個檔案就是“裝置檔案”)
2.open()是庫裡面的函數,是以庫裡面的一部分代碼會被調用
3.這時候庫裡面會執行異常指令,執行這些異常指令最後導緻的結果就是産生“系統調用”進入核心,這個時候從使用者空間進入了核心空間
** 4、進入核心後,核心會接受到這個異常并開始處理,處理的結果是找到對應的驅動程式**
5、找到對應的驅動後,驅動程式中的xxx_open()函數就會被調用。因為驅動程式是在核心裡面執行的,它的運作權限就比較大,是以在驅動的open()函數就可以對硬體裝置進行操作了。
根據上面的5個步驟,最終open()函數和xxx_open()函數對應起來。
上述是open()函數調用的過程,open一個檔案之後,緊接着就會調用read() write() close()等函數,他們的調用過程和open()函數是一樣的。
驅動架構中的問題
1.C庫是怎麼進入核心呢?open,read,write實作的實質是什麼?
執行
swi val
(彙編指令),引發一個異常,相當于中斷一樣。當發生這個異常的時候,就會進入核心的異常處理函數裡面。
2.在庫中執行一段異常指令就會通過系統調用進入核心,核心是怎麼找到對應的驅動程式的呢?**
上層的open函數調用之後是怎麼找到具體的驅動程式并且調用該驅動程式中的open函數的——裝置檔案中的主從裝置号。是以我們從這裡也得出了一個非常重要的結論:主從裝置在跟裝置檔案綁定的同時也跟具體的驅動程式綁定在一起。
裝置檔案:從文字上看就是裝置的檔案,這類檔案就是專門給硬體裝置服務的檔案,而且這類檔案都存在
/dev
目錄下,每當我們向核心中加一個裝置驅動程式,這時候就會在相應的
/dev
目錄下生成相應的裝置檔案。總結起來:裝置檔案就是跟驅動程式對應起來的,是上層應用通路驅動的接口。下面我們就來看一下這個裝置檔案的屬性:
- 通路權限之前的字母是b或c,分别表示塊裝置和字元裝置。
- 裝置檔案沒有檔案長度,而增加了另外的兩個值,分别是主裝置号和從裝置号。二者共同形成一個唯一的号碼,核心可由此查找對應的裝置驅動程式。
- 之是以給裝置檔案配置設定名稱,是因為使用者更容易記憶符号名而不是數字,但名稱無法表示裝置檔案的實際功能,這主要是通過主從裝置号表示一個裝置的,裝置檔案所處的目錄也與其功能不相幹。
裝置檔案中非常重要的3個屬性:
1.裝置類型:c和b,c代表這個裝置檔案對應的裝置驅動程式是一個字元裝置驅動程式,b代表這個裝置檔案對應的裝置驅動程式是一個塊裝置驅動程式。
2.—主裝置号(範圍:1~254):辨別一類驅動程式。通過前面這個圖我們可以看出主裝置号204是辨別序列槽這一類驅動程式;主裝置号31辨別mtd這一類驅動程式。那這裡就有一個問題了,我們知道一台電腦上不止一個序列槽,這多個序列槽的驅動程式都是用主裝置号204來辨別,那我們怎麼從中找到某一個具體的序列槽驅動程式呢,這個時候就需要我們的從裝置号了。
3.從裝置号(範圍:0~255):對應一類驅動程式中某一個具體的驅動程式。當我們通過204這個主裝置号找到序列槽這一類驅動程式後,再通過從裝置号找到具體的某一個序列槽的驅動程式。
3.系統調用接口的作用(system call interface)
在異常處理函數裡面,根據發生異常的原因,調用不同的處理函數。
例如:使用open函數,則傳進來的值為val1(swi val1);使用read函數,則傳進來的值為val2(swi val2);使用write函數,則傳進來的值為val3(write val3)。核心裡的系統調用接口(system callinterface),會根據傳進來不同的值,去調用sys_open、sys_read和sys_write。
總結
Sytem call interface系統調用接口是 決定 使用哪一個函數(read(),write(),open()……)
VFS虛拟檔案系統 是 決定 調用哪一個驅動程式的open()函數(led_open()、memdev_open()……)
file_operations
file_operations
:是一個函數指針的集合
- 應用程式和VFS之間的接口是系統調用,而VFS與磁盤檔案系統以及普通裝置之間的接口是file_operations結構體成員函數;file_operations結構體中成員函數是字元裝置驅動與核心的接口,是使用者空間對Linux進行系統調用最終的落實者,這個結構體包含對檔案打開,關閉,讀寫,控制的一系列成員函數。
- 由于字元裝置的上層沒有磁盤檔案系統,是以字元裝置的file_operations成員函數就直接由裝置驅動提供了,file_operations正是字元裝置驅動的核心。
- 而對于塊裝置而言,ext2,fat,jffs2等檔案系統中會實作對VFS的file_operations成員函數,裝置驅動層将看不到file_operations的存在。磁盤檔案系統和裝置驅動會将磁盤上檔案的通路最終轉換成對磁盤上柱面和扇區的通路。
file_operations成員
static const struct file_operations XXX_fops =
{
.owner = THIS_MODULE,
.llseek = XXX_llseek,
.open = XXX _open,
.read = XXX _read,
.write = XXX _write,
.ioctl = XXX _ioctl,
.release = XXX _release,
};
struct module *owner
第一個 file_operations 成員根本不是一個操作; 它是一個指向擁有這個結構的子產品的指針. 這個成員用來在它的操作還在被使用時阻止子產品被解除安裝. 幾乎所有程式中, 它被簡單初始化為
THIS_MODULE
, 一個在
<linux/module.h
> 中定義的宏.
cloff_t (*llseek) (struct file *, loff_t, int);
檔案定位函數,合法時傳回檔案的目前位置,不合法傳回-EINVAL
第一個參數為file指針,第二個為請求偏移量,第三個為檔案定位的起始位址:一般為0或1,0表示檔案開頭,1表示目前位置
讀函數,利用copy_to_user()函數讓核心讀取使用者空間的資料,并傳回通路的位元組數
寫函數利用copy_from_user()函數讓使用者向檔案寫入資料,并傳回寫入的位元組數
執行I/O控制指令
打開檔案
(7)int (*release) (struct inode *, struct file *) ;
關閉檔案