昨天我們對字元裝置進行了初步的了解,并且實作了簡單的字元裝置驅動,今天我們繼續對字元裝置的某些方法進行完善。
今天我們會分析到以下内容:
1. 字元裝置控制方法實作
2. 揭秘系統調用本質
在昨天我們實作的字元裝置中有open、read、write等方法,由于這些方法我們在以前編寫應用程式的時候,相信大家已經有所涉及是以就沒單獨列出來分析,今天我們主要來分析一下我們以前接觸較少的控制方法。
1. 裝置控制簡介
1. 何為裝置控制:我們所接觸的大部分裝置,除了讀、寫、打開關閉等方法外,還應該具有控制方法,比如:控制電機轉速、序列槽配置波特率等。這就是對裝置的控制方法。
2. 使用者如何進行裝置控制:類似與我們在使用者空間使用read、open等函數對裝置進行操作,我們在使用者空間對裝置控制的函數是ioctl其原型為 int ioctl(int fd, int cmd, …)//fd為要控制的裝置檔案的描述符,cmd是控制指令,…依據第二個參數類似與我們的printf等多參函數。
3. Ioctl調用驅動那個函數:在我們的使用者層進行ioctl調用的時候驅動會根據核心版本不同調用不同的函數,有以下:
1) 2.6.36以前的核心版本會調用 long (*ioctl) (struct inode*,struct file *, unsigned int, unsigned long);
2) 2.6.36以後的核心會調用 long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
2. Ioctl實作
1. 控制指令解析:我們剛才說到ioctl進行控制的時候有個cmd參數其為int類型的也就是32位,我們的linux為了讓這32位更加有意義,所表示的内容更多,是以将其分為了下面幾個段
1) Type(類型/幻數8bit):表明這是屬于哪個裝置的指令
2) Number(序号8bit):用來區分統一裝置的不同指令
3) Direction(2bit):參數傳遞方向,可能的取值,_IOC_NODE(沒有資料傳輸)、_IOC_READ(從裝置讀)、_IOC_WRITE(向裝置寫)
4) Size(13/14bit()):參數長度
2. 定義指令:我們的控制指令如此複雜,為了友善我們的linux系統提供了固定的宏來解決指令的定義,具體如下:
1) _IO(type,nr); :定義不帶參數的指令
2) _IOR(type,nr,datatype); :從裝置讀參數指令
3) _IOW(type,nr,datatype); :向裝置寫入參數指令
下面定義一個向裝置寫入參數的指令例子
#define MEM_CLEAR _IOW(‘m’,0,int)//通常用一個字母來表示指令的類型
l 揭秘系統調用本質
由于我自己的PC的調用過程不太熟悉,下面以arm的調用過程分析一下我們使用者層調用read之後發生了什麼,是怎麼調用到我們驅動寫的read函數的呢,我們下面進行深入剖析。
1. 代碼分析
我們首先使用得到arm上可執行的應用程式 arm-linux-gcc -g -static read_mem.c -o read_mem 然後使用 arm-linux-objdump -D -S read_mem >dump 得到彙編檔案,我們找到main函數的彙編實作
上面我們發現read最終調用了__libc_read函數我們繼續在彙編代碼中找到該函數
在上面代碼中大部分彙編指令都知道用法,但是svc調用引起注意,通過查閱資料才發現,我們應用程式通過svc 0x00000000可以産生異常,進入核心空間。
然後呢,系統處理異常,這中間牽扯好多代碼還有中斷的一些知識,我們找時間在專門分析,總之經過一大堆的處理最後它會跳到entry-common.S中的下面代碼
View Code
該段代碼中我們先會擷取系統調用的标号剛才讓大家記住的3,然後呢會去查找sys_call_table我們找到
在calls.S中我們找到了下面東西(列出部分)
我們發現我們剛才記住的數字3剛好對應的是sys_read,在read_write.c中我們可以找到sys_read函數
2. 過程總結
通過上面的分析我們已經了解的read函數的調用基本過程,下面我們将read函數的調用過程在進行總結:
1. 尋找svc異常總體入口,并進入核心空間
2. 取出系統調用的标号
3. 根據系統調用标号,在sys_call_table中找到對應的系統調用函數
4. 根據系統函數比如sys_read找到對應的虛拟檔案系統的read
5. 虛拟檔案系統在調用驅動的read。
至此我們的分析到此結束,當然整個過程中還有一部分異常處理沒有說到,我們在分析中斷的時候一塊分析。
今天的分析到此結束,感謝大家的關注。