您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 管理学资料 > Ch5-字元装置驱动程式的进阶操作
Ch5-字元裝置驅動程式的進階操作Outline•Introduction•5.1ioctl5.2推延式I/O•5.3poll與select5.4非同步通知•5.5改變裝置的存取點5.6裝置檔的存取控制•5.7回溯相容性5.8速查參考5-Introduction•驅動程式除了讀寫動作之外,通常還需要提供各種控制硬體的能力,而控制動作通常是透過ioctl作業方法來實施。•不過,並非所有的裝置都採用ioctl控制方式,有的驅動程式採用了另一種控制技術(預先定義一組特殊序列來當成控制命令),例如:ttych-5.1.7•ioctl()系統呼叫為驅動程式提供了一個下達“裝置特有的命令(device-specificcommand)”的管道。這類命令的定義與功能是隨硬體裝置而定:–設定暫存器的狀態–進入或離開某作業某作業模式•ioctl()的作用:控制I/O通道5.1-ioctl•User-space的觀點來看,ioctl()系統呼叫的函式原形如下:intioctl(intfd,intcmd,…)fd:filedescriptor,檔案描述單元cmd:控制命令…:並非代表不定量引數,而是一個可有可無的引數-習慣上表示為char*argp(為了通過編譯時期的型別檢查typechecking)•ioctl作業方法會收到下列宣告的引數:int(*ioctl)(structinode*inode,structfile*file,unsignedintcmd,unsignedlongarg)inode指標指向對應到應用程式傳來的fd…filp指標指向一個代表裝置節點的file結構cmd的值等於ioctl()系統呼叫的第二引數如果應用程式發出的ioctl()系統呼叫有第三個引數,則ioctl作業方法會收到一個unsignedlong型別的arg引數,否則無意義5.1-ioctl•由於編譯器無法檢查而外引數的型別,因此,當應用程式傳遞了一個無效引數給ioctl(),驅動程式也要到執行期才會知道錯誤。這項缺點是ioctl系統定義使然,但卻是ioctl()為了提供通用功能性的必然代價。•大部份驅動程式實作出來的ioctl作業方法都包含了一個switch敘述,並依據cmd引數來選擇正確的處理程序。•不同的命令(cmd)有不同的代表值,通常在標頭檔裡定義一組符號來代表各個命令值ex:scull驅動程式就在scull.h標頭檔裡宣告一組命令代號5.1.1-選擇ioctl命令編號•編寫ioctl的具體程式之前,必須先為各個命令挑選對應的編號。最簡單的選擇“從1開始逐一分配”是行不通的。•系統上,每個命令的編號都必須是獨一無二的:–以免正確命令被下達到錯誤裝置所造成的災難。–若沒有重複的ioctl命令編號,則搞錯對象的程式就會收到EINVAL錯誤,不至於…。•初版的linux採用16bits的編號:–高八位元代表裝置個體的“魔數(magicnumber)”–低八位元則是供裝置內部使用的“序號(sequencenumber)”–同系統上,沒有相同魔數的裝置。同一裝置裡沒有重複的序號。clueless說:這樣的劃分是他一時的無知#defineSCULL_IOCTL10x6b01#defineSCULL_IOCTL20x6b025.1.1-選擇ioctl命令編號•新版的劃分法則:–查閱include/asm/ioctl.h(定義所要使用的各個位元欄,包括:類型,魔數,流水號,傳輸方向)–查閱Documentation/ioctl-number.txt(列出所有已經分配給核心的魔術,及解釋了為何應該採用新法則)•新的劃分法使用四個位元欄位定義在linuxioctl.h–type(magicnumber):自己挑選一個符合規定的數值,並用於整個驅動程式。欄位長度為_IOC_TYPEBITS(8-bits)–number(ordinalnumber):可稱為序號,此欄位的長度為_IOC_NRBITS(8-bits)–direction:傳輸方向,代表資料的流向。包括(_IOC_NONE,_IOC_READ,_IOC_WRITE,_IOC_READ|_IOC_WRITE)。英站在應用程式的觀點來看。–size:使用者資料量。此欄位的寬度隨硬體平台而定。(8-bits~14-bits)建議在8-bits以下來保持可移值性。5.1.1-選擇ioctl命令編號•整數引數的傳遞方式有兩種,一是透過指標,二是直接給明確數值;ioctl()的普遍慣例,應該採用指標來交換數值。•對於系統呼叫的回傳值有不成文的慣例:負值代表錯誤且被用來設定user-space的errno變數,正值的意義由系統呼叫自己決定。•連動atomic(在實務上,驅動程式偶而會需要一口氣完成原本是分離的兩項動作,尤其是應用程式需要設定,或釋放lock時):–X(交換)=G(取得)+S(設定)H(移位)=T(通知)+Q(查詢)/*Smeans“Set”throughaptr,(設定)*Tmeans“Tell”directlywiththeargumentvalue(通知)*Gmeans“Get”:replybysettingthroughapointer(取得)*Qmeans“Query”:responseisonthereturnvalue(查詢)*Xmeans“eXchange”:GandSatomically(交換)*Hmeans“sHift”:TandQatomically(移位)*/#defineSCULL_IOC_MAGIC'k'#defineSCULL_IOCRESET_IO(SCULL_IOC_MAGIC,0)/*Use'k'asmagicnumber*/#defineSCULL_IOCSQUANTUM_IOW(SCULL_IOC_MAGIC,1,scull_quantum)#defineSCULL_IOCSQSET_IOW(SCULL_IOC_MAGIC,2,scull_qset)#defineSCULL_IOCTQUANTUM_IO(SCULL_IOC_MAGIC,3)#defineSCULL_IOCTQSET_IO(SCULL_IOC_MAGIC,4)#defineSCULL_IOCGQUANTUM_IOR(SCULL_IOC_MAGIC,5,scull_quantum)#defineSCULL_IOCGQSET_IOR(SCULL_IOC_MAGIC,6,scull_qset)#defineSCULL_IOCQQUANTUM_IO(SCULL_IOC_MAGIC,7)#defineSCULL_IOCQQSET_IO(SCULL_IOC_MAGIC,8)#defineSCULL_IOCXQUANTUM_IOWR(SCULL_IOC_MAGIC,9,scull_quantum)#defineSCULL_IOCXQSET_IOWR(SCULL_IOC_MAGIC,10,scull_qset)#defineSCULL_IOCHQUANTUM_IO(SCULL_IOC_MAGIC,11)#defineSCULL_IOCHQSET_IO(SCULL_IOC_MAGIC,12)/*HARDRESET命令,可將模組的用量計次歸零*/5.1.2-ioctl的回傳值•ioctl作業方法的具體內容,主要是用來分辨命令編號(cmd引數)的switch敘述。•如果cmd不符合任何命令編號,則default應做?–許多核心合適採取的行為是回傳-EINVAL(InvalidArgument),這是合理的。–但,POSIX標準卻規定回傳-ENOTTY(Notatypewriter)這不甚合理,但libc6已將訊息改成比較合理的“Inappropriateioctlfordevice”5.1.3-預先定義的ioctl命令•雖然ioctl()系統呼叫的主要作用對象是硬體裝置,但是核心本身仍能辨認少數幾個命令(預設命令)。因此,當你挑選的ioctl命令編號剛好與預定命令相同,則你寫出來的ioctl作業方法將永遠收不到該命令,而應用程式也會因為發出衝突的ioctl命令而遭遇到意外。•預定命令分為三大類:–可作用於任何檔案(正常檔,裝置檔,FTFO或socket)的命令–只對正常檔案有作用的命令–只能用於特定檔案系統類型的命令•驅動程式設計者只須注意第一類命令(其魔數是“T”)5.1.3-預先定義的ioctl命令•以下是核心內建的ioctl命令,可作用於任何檔案:–FIOCLEX:設立close-on-exec旗標。當某行程開始執行(使用exec系統呼叫)–FIONCLEX:撤銷close-on-exec旗標–FIOASYNC:設立或撤銷檔案的“非同步通知”–FIONBIO:”FileIOctlNonblockI/O”此命令會修改filp-f_flags裡的O_NONBBOCK旗標。要下達此命令的應用程式,必須在ioctl()的第三引數註明他到底想要執行“設立”或“撤銷”的動作。–fcntl()系統呼叫也會修改O_NONBBOCK旗標狀態。fcntl()與ioctl()非常類似,這兩者會被分開主要是基於歷史因素,當初UNIX研發者再面對I/O控制作業的問題十,決定將“檔案”與“裝置”視為不同的東西,在當時唯一具有ioctl作業方法的裝置只有裝端機(tty)。這解釋了為何ioctl()沒收到正確命令時,傳回值竟是-ENOTTY。5.1.4-ioctl的額外引數之用法•在開始研究scull如何實作ioctl作業方法之前,有必要先搞清楚如何使用它的額外引數(第三引數)。–若該引數為整數時,那就直接拿來用。–如果是指標,就必須多費點心。–若指標指向user-space的位址,必須先確定該位址是有效的,而且其對應的記憶頁目前已經映射(mapped)到系統記憶體(RAM)。若核心程式試圖存取一個超出範圍的位址,處理器會主動觸發一次異常(exception)。–2.2版之後,核驗位址合法性的工作,交由access_ok()函式來進行,此函式定義在asm/uaccess.h。在2.2版前使用者必須自行核驗。–access_ok()回傳值:1代表成功(可存取),0代表失敗(不能存取)。5.1.4-ioctl的額外引數之用法•access_ok()函式:–type必須是VERIFY_READ或VERIFY_WRITE的其中之一(取決於想對user-space進行的動作是讀入或寫出)–addr引數是要檢查的user-space位址–size是檢查範圍(以byte為計算單位)•access_ok()值得注意的地方:–access_ok()並非徹底檢驗指定範圍內的每一個位址,而是檢驗受查記憶區是否在行程的合理存取範圍(不屬kernel-space)–大部份的驅動程式並不需要刻意呼叫access_ok()(記憶體存取程序會幫忙處理)intaccess_ok(inttype,constvoid*addr,unsignedlongsize);•在呼叫access_ok()之後,驅動程式就可放心進行實際的傳輸。除了使用copy_from_user以及copy_to_user函式之外,asm/uaccess.h還提供了一組針對常用資料規格而設計的傳輸工具:put_user(datum,ptr),__put_user(datum,ptr);get_user(datum,ptr)……/*分離出type和number位元欄位,如果遇到錯誤的cmd,直接傳回ENOTTY*/if(_IOC_TYPE(cmd)!=SCULL_IOC_MAGIC)return-ENOTTY;if(_IOC_NR(cmd)SCULL_IOC_MAXNR)return-ENOTTY;/*direction是一個位元遮罩,而VERIFY_WRITE代表雙向傳輸*`Type‘是從user-oriented來看*access_ok卻是從kernel-oriented來看
本文标题:Ch5-字元装置驱动程式的进阶操作
链接地址:https://www.777doc.com/doc-3218882 .html