天天看點

Linux核心分析(六)----字元裝置控制方法實作|揭秘系統調用本質

昨天我們對字元裝置進行了初步的了解,并且實作了簡單的字元裝置驅動,今天我們繼續對字元裝置的某些方法進行完善。

今天我們會分析到以下内容:

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中的下面代碼   

Linux核心分析(六)----字元裝置控制方法實作|揭秘系統調用本質
Linux核心分析(六)----字元裝置控制方法實作|揭秘系統調用本質

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。

至此我們的分析到此結束,當然整個過程中還有一部分異常處理沒有說到,我們在分析中斷的時候一塊分析。

今天的分析到此結束,感謝大家的關注。

繼續閱讀