天天看點

十天學Linux核心之第九天---向核心添加代碼

  睡了個好覺,很晚才起,好久沒有這麼舒服過了,今天的任務不重,是以壓力不大,呵呵,現在的天氣真的好冷,不過實驗室有空調,我還是喜歡待在這裡,有一種不一樣的感覺,在寫了這麼多天之後,自己有些不懂的頁漸漸的豁然開朗了嗎,而且也交到了一些朋友,真是相當開心啊。今天将介紹一下向核心中添加代碼,一起來看看吧~

  先來熟悉一下檔案系統,通過/dev可以通路Linux的裝置,我們以men裝置驅動程式為例來看看随機數是如何産生的,源代碼在dirvers/char/mem.c上可以檢視

  那麼上述程式的filps和fop是什麼呢?實際上filp隻是一個檔案結構指針,而fop是一個file_operations結構指針,核心通過file_operations結構來确定操作檔案時要調用的函數,下面的file_operations結構用于随機裝置驅動的部分内容,代碼在include/linux/fs.h上可以檢視到:

  q驅動程式所實作的函數必須符合file_operations結構中所列出的函數原型,代碼在dirvers/char/random.c上可以檢視:

  如果裝置驅動程式在核心空間運作,但是緩沖區卻位于使用者空間,那我們該如何才能安全通路buf中的資料呢,下面來說下資料在使用者空間和核心空間之間的奧秘,Linux提供的copy_to_user()和copy_from_user()使得驅動程式可以在核心空間和使用者空間上傳遞資料,在read_random()中,通過extract_entropy()函數來實作這個功能,下面代碼在dirvers/char/random.c上可以檢視(下面的代碼沒有敲完,主要是不是很懂,望大神指教)

  核心空間和使用者空間的程式可能都需要使用已經獲得的随機數,核心空間的程式可以通過不設定标志位來避免函數copyto_user()帶來的額外開銷。除了通過裝置驅動程式向核心添加代碼之外,還有别的方式 的,使用者空間可以通過系統調用來通路核心服務程式和系統硬體,這裡不多闡釋,都知道有這回事就行了。

  下面我們來介紹怎麼去編寫源代碼,當我們去編寫一個複雜的裝置驅動程式時,也許要輸出驅動程式中定義的某些符合,以便讓核心其它子產品使用,這些通常被用在低級的驅動程式中,以便根據這些基本的函數來建構更進階的驅動程式,在Linux2.6核心中,code monkey可以用如下兩個宏輸出符号,代碼在include/linux/module.h中檢視:

  目前為止,我們介紹的裝置 驅動程式都是主動操作,或者對裝置的資料進行讀寫操作,那麼它的功能不止這些的時候會怎麼樣呢?在Linux中,裝置驅動程式解決這些問題的典型方式就是使用ioctl。ioctl是裝置驅動程式中對裝置的I/O通道進行管理的函數。所謂對I/O通道進行管理,就是對裝置的一些特性進行控制,例如序列槽的傳輸波特率、馬達的轉速等等。

調用個數如下:

其中fd就是使用者程式打開裝置時使用open函數傳回的檔案标示符,cmd就是使用者程式對裝置的控制指令,至于後面的省略号,那是一些補充參數,一般最多一個,有或沒有是和cmd的意義相關的。ioctl函數是檔案結構中的一個屬性分量,就是說如果你的驅動程式提供了對ioctl的支援,使用者就可以在使用者程式中使用ioctl函數控制裝置的I/O通道。

ioctl指令号:

dir:

  代表資料傳輸的方向,占2位,可以是_IOC_NONE(無資料傳輸,0U),_IOC_WRITE(向裝置寫資料,1U)或_IOC_READ(從裝置讀資料,2U)或他們的邏輯或組合,當然隻有_IOC_WRITE和_IOC_READ的邏輯或才有意義。

type:

描述了ioctl指令的類型,8位。每種裝置或系統都可以指定自己的一個類型号,ioctl用這個類型來表示ioctl指令所屬的裝置或驅動。一般用ASCII碼字元來表示,如 'a'。

  nr:

ioctl指令序号,一般8位。對于一個指定的裝置驅動,可以對它的ioctl指令做一個順序編碼,一般從零開始,這個編碼就是ioctl指令的序号。

  size:

ioctl指令的參數大小,一般14位。ioctl指令号的這個資料成員不是強制使用的,你可以不使用它,但是我們建議你指定這個資料成員,通過它我們可以檢查使用者空間資料的大小以避免錯誤的資料操作,也可以實作相容舊版本的ioctl指令。

ioctl傳回值:

   ioctl函數的傳回值是一個整數類型的值,如果指令執行成功,ioctl傳回零,如果出現錯誤,ioctl函數應該傳回一個負值。這個負值會作為errno值回報給調用此ioctl的使用者空間程式。關于傳回值的具體含義,請參考<linux/errno.h>和<asm/errno.h>頭檔案。

ioctl參數:

  首先要說明這個參數是有使用者空間的程式傳遞過來的,是以這個指針指向的位址是使用者空間位址,在Linux中,使用者空間位址是一個虛拟位址,在核心空間是無法直接使用它的。為了解決在核心空間使用使用者空間位址的資料,Linux核心提供了以下函數,它們用于在核心空間通路使用者空間的資料,定義在<asm/uaccess.h>頭檔案中:

copy_from_user和copy_to_user一般用于複雜的或大資料交換,對于簡單的資料類型,如int或char,核心提供了簡單的宏來實作這個功能:

  

cmd參數如何得出:

  一個cmd參數被分為4段,每段都有其特殊的含義,cmd參數在使用者程式端由一些宏根據裝置類型、序列号、傳送方向、資料尺寸等生成,這個整數通過系統調用傳遞到核心中的驅動程式,再由驅動程式使用解碼宏從這個整數中得到裝置的類型、序列号、傳送方向、資料尺寸等資訊,然後通過switch{case}結構進行相應的操作。解釋一下四部分,全部都在<asm-generic/ioctl.h>和ioctl-number.txt這兩個文檔有說明的 。

1)幻數:說得再好聽的名字也隻不過是個0~0xff的數,占8bit(_IOC_TYPEBITS)。這個數是用來區分不同的驅動的,像裝置号申請的時候一樣,核心有一個文檔給出一些推薦的或者已經被使用的幻數

2)序數:用這個數來給自己的指令編号,占8bit(_IOC_NRBITS),我的程式從1開始排序。

3)資料傳輸方向:占2bit(_IOC_DIRBITS)。如果涉及到要傳參,核心要求描述一下傳輸的方向,傳輸的方向是以應用層的角度來描述的。

_IOC_NONE:值為0,無資料傳輸。

_IOC_READ:值為1,從裝置驅動讀取資料。

_IOC_WRITE:值為2,往裝置驅動寫入資料。

_IOC_READ|_IOC_WRITE:雙向資料傳輸。

4)資料大小:與體系結構相關,ARM下占14bit(_IOC_SIZEBITS),如果資料是int,核心給這個賦的值就是sizeof(int)。

 ioctl如何實作:

  在驅動程式中實作的ioctl函數體内,實際上是有一個switch{case}結構,每一個case對應一個指令碼,做出一些相應的操作。怎麼實作這些操作,這是每一個程式員自己的事情,因為裝置都是特定的,這裡也沒法說,關鍵在于怎麼樣組織指令碼,因為在ioctl中指令碼是唯一聯系使用者程式指令和驅動程式支援的途徑。

  指令碼的組織是有一些講究的,因為我們一定要做到指令和裝置是一一對應的,這樣才不會将正确的指令發給錯誤的裝置,或者是把錯誤的指令發給正确的裝置,或者是把錯誤的指令發給錯誤的裝置。這些錯誤都會導緻不可預料的事情發生,而當程式員發現了這些奇怪的事情的時候,再來調試程式查找錯誤,那将是非常困難的事情

是以在Linux核心中是這樣定義一個指令碼的:

____________________________________

| 裝置類型 | 序列号 | 方向 |資料尺寸|

|----------|--------|------|--------|

| 8 bit    |  8 bit |2 bit |8~14 bit|

  這樣一來,一個指令就變成了一個整數形式的指令碼。但是指令碼非常的不直覺,是以Linux Kernel中提供了一些宏,這些宏可根據便于了解的字元串生成指令碼,或者是從指令碼得到一些使用者可以了解的字元串以标明這個指令對應的裝置類型、裝置序列号、資料傳送方向和資料傳輸尺寸。

  在核心中是無法直接通路使用者空間位址資料的。是以凡是從使用者空間傳遞過來的指針資料,務必使用核心提供的函數來通路它們。這裡有必要再一次強調的是,在核心子產品或驅動程式的編寫中,我們強烈建議你使用核心提供的接口來生成并操作ioctl指令号,這樣可以對指令号賦予特定的含義,使我們的程式更加的健壯;另一方面也可以提高程式的可移植性。

  最後我們來介紹一下添加代碼後的編譯和調試,在核心中添加代碼後就需要不斷運作,修複錯誤,我們知道當對/proc檔案系統進行讀寫操作時,它的每一個結點都連結到一個核心函數,在Linux2.6核心中,要想你的裝置能夠被通路,首先就要在/proc檔案系統中建立一個入口,這個可以通過creat_proc_read_entry()來實作,代碼在include/linux/proc_fs.h上檢視:

*name是結點在/proc檔案系統的入口,*base指向設定proc檔案的目标路徑,如果它的值為NULL,表示該檔案就在/proc目錄下,讀取該檔案可以調用*read_proc指向的函數。這裡也不多加闡釋了,整個也是很簡單的過程。

  小結

  今天的重點是iotcl函數了,其中還有很多向核心中添加代碼的細節沒有講到,主要是這些都涉及到過多的操作,需要大家多看源代碼并且多動手在Linux上操作才能完全掌握,,今天寫的一些也借鑒了一些大牛的文章,總之 收獲很多,最後幾天了,真的是很開心啦,和大家一起分享真的很快樂的~~

繼續閱讀