Chap3-字元裝置驅動程式Outline•Introduction•3.1scull的設計藍圖3.2主編號與次編號•3.3檔案架構3.4file結構•3.5open與release3.6Scull的記憶體用法規劃•3.7相競狀況3.8read與write•3.9tryscull3.10devfs檔案系統•3.11回溯相容性3.12速查參考3--Introduction•本章目標:寫出一個完整的字元裝置驅動程式(chardevicedriver)--簡稱chardriver•終極目標:寫出一個模組化的chardriver•範例:scull--(SimpleCharacterUtilityforLoadingLocalities)–Makefile,main.c,access.c,empty.c,pipe.c,scull.h,scull.init,scull_load,scull_unload,alpha.checkthem•scull的作用是“讓使用者可把一塊記憶區當成字元裝置來使用”scull所驅動的目標裝置是一塊記憶區–不需依賴任何“特殊”硬體–只要有linux平台就可以編譯與執行–未提供任何實用功能,只展示核心與chardriver之間軟體介面3.1--scull的設計藍圖•定義驅動程式要提供哪些功能給user-sapce的程式–可循序存取(字元裝置)or可隨機存取(區塊裝置)–模擬單一裝置(ex:一機多體or多個同類裝置)•Scull所模擬出的每一種裝置,分別由不同類型的模組予以實現(相同的mechanism差別在於policy的不同)–scull0~scull3四個由記憶區所構成的裝置,兼具“共通”“持續”–scullpipe0~scullpipe3四個FIFO裝置(blocking與nonblocking)–Scullsingle一次只容許一個被行程存取–Scullpriv允許每個終端機都有權開啟一次,分屬不同行程–Sculluid允許開始多次,限同一使用者。(回傳錯誤碼)–Scullwuid允許開始多次,限同一使用者。(推延,等待)Ch_5.2.5Ch_5.63.2--主編號與次編號•主編號(majornumber)(0~255)–代表裝置所配合的驅動程式–當核心收到open()系統呼叫時,就是依據“主編號”來選擇驅動程式•次編號(minornumber)(0~255)–驅動程式以次編號來辨認同類裝置的個體–核心本身用不到,只有驅動程式自己才知道次編號的意義•當使用者要存取字元裝置時,必須透過檔案系統裡的“代表名稱”特殊檔(specialfile)、裝置檔(devicefile)、檔案系統樹的節點(node),集中在/dev/目錄下。•裝置類型:–“c”代表chardriver的特殊檔–“b”代表blockdriver的裝置檔3.2--主編號與次編號ls–al/dev/|lessbrw-rw----1rootdisk66,72Apr112002sdak8brw-rw----1rootdisk66,73Apr112002sdak9crw-r--r--1rootroot253,1Mar122:58scull1crw-r--r--1rootroot253,2Mar122:58scull2crw-r--r--1rootroot253,3Mar122:58scull3brw-rw----1rootdisk8,0Apr112002sdacrw-rw----1rootuucp154,18Apr112002ttySR18裝置類型主編號次編號代表名稱3.2--主編號與次編號•檔案系統製作裝置節點的命令是mknod,必須有特權身分(root)才能使用此工具。至少需要四個引數…–代表名稱裝置類型主編號次編號mknod/dev/antc2520•像任何儲存在磁碟上的普通檔案一樣,mknod所產生的裝置節點會被保存下來,除非刻意刪除它們。用一般的rm命令即可辦到…不使用時未刪除及佔用空間rm/dev/ant3.2.1--隨機取得主編號•大部份常見的裝置幾乎都有固定的主編號,可在核心源碼樹的Documentation/devices.txt檔案內找到一份“裝置-主編號”對照表。挑選可用主編號不易less/usr/src/linux-2.4.20/Documentation/devices.txt•“實驗性或自家使用”的主編號:–60~63、120~127、240.254真正公開給大眾使用的驅動程式不該使用這些範圍內的主編號•“隨機索取主編號”呼叫register_chrdev()–定義在linux/fs.hless/usr/src/linux-2.4.20/include/linux/fs.hexternintregister_chrdev(unsignedint,constchar*,structfile_operations*);externintunregister_chrdev(unsignedint,constchar*);3.2.1--隨機取得主編號•在呼叫register_chrdev()時:–major引數給‘0’:=回傳值為‘0&255’核心分配的主編號–major引數給‘0&255’:=回傳值為‘0’表示核心同意你的要求–發生錯誤時:=回傳值為‘負數’•如果你的驅動程式會被用於廣大群眾,或者有可能被納入正式核心,則須設法申請專用的主編號。3.2.1--隨機取得主編號cat/proc/devicesCharacterdevices:1mem2pty226drm253scull254pcmciaBlockdevices:2fd3ide03.2.1--隨機取得主編號•缺點:–因為模組分配到的主編號不一定每次都一樣,所以無法事先建立裝置節點。不能作出必要時才載入“load-on-demand”的驅動程式•因此,光靠insmod是不夠的,還必須查閱/proc/devices的內容後,找出其隨機取得的‘主編號’,再製作成適當的裝置節點(甚至要刪除上次留下的無用節點)•可寫成一個shellscript來一次解決利用awk之類工具–範例:scull_load==每次開機時透過/etc/rc.local來執行scull_unload==卸載模組及清理/dev/下的相關節點scull.init==由系統開機/關機程序自動載入/卸載模組。接受傳統命令start,stop,restartCh_11#!/bin/shmodule=scull“scull_loaddevice=scullmode=664#Group:sincedistributionsdoitdifferently,lookforwheelorusestaffifgrep'^staff:'/etc/group/dev/null;thengroup=staffelsegroup=wheelfi#invokeinsmodwithallargumentswegot#anduseapathname,asnewermodutilsdon'tlookin.bydefault/sbin/insmod-f./$module.o$*||exit1major=`cat/proc/devices|awk\\$2==\$module\{print\\$1}`#Removestalenodesandreplacethem,thengivegidandperms#Usuallythescriptisshorter,it'sscullthathasseveraldevicesinit.rm-f/dev/${device}[0-3]mknod/dev/${device}0c$major0mknod/dev/${device}1c$major1mknod/dev/${device}2c$major2mknod/dev/${device}3c$major3ln-sf${device}0/dev/${device}chgrp$group/dev/${device}[0-3]chmod$mode/dev/${device}[0-3]#!/bin/shmodule=scull“scull_unloaddevice=scull#invokermmodwithallargumentswegot/sbin/rmmod$module$*||exit1#Removestalenodesrm-f/dev/${device}/dev/${device}[0-3]rm-f/dev/${device}privrm-f/dev/${device}pipe/dev/${device}pipe[0-3]rm-f/dev/${device}singlerm-f/dev/${device}uidrm-f/dev/${device}wuid•scull_load&scull_unload也適用其他其他驅動程式,只需重新定義變數,並稍微調整mknod那幾行即可。•因為指令稿需要特權身分才能執行,而新建出來的特殊檔自然會屬於root。但裝置節點的存取權限通常不是這樣,所以要修改權限的擁有權。3.2.2—移除驅動程式•在模組被卸載之前,他必須先釋放主編號,而這動作可由unregister_chrdev()完成,應該在模組的清理函式理呼叫他:–major表示要被釋放的主編號;name是當初註冊的名稱必須與當初呼叫register_chrdev()所用的引述一致•對於動態取得主編號的驅動程式,如果在卸載模組之後沒有順便除掉相關的/dev結點,就可能產生意料外的錯誤。譬如,已經沒有作用的/dev/framegrabber可能在一個月後指向火災警報器如果兩個驅動程式都使用隨機取得的主編號intunregister_chrdev(unsignedintmajor,constchar*name):3.2.3--副編號(dev_t&kdev_t)•每當核心要呼叫驅動程式,都必須讓驅動程式知道,他應該作用在哪一個裝置上;驅動程式以裝置編號來辨認其作用對象。(裝置編號=主編號+次編號)•dev_t–Unix以dev_t(代表“devicetype”)作為裝置編號的資料型別–定義在sys/types.h–更改不易,因太多應用程式“知道”其內部結構…•kdev_t–Linux核心內部採用另一種不同的資料型別–定義在linux/kdv_t.h–‘黑盒子’每個核心函式都不必知道其內部結構,而應用程式可以不知道kdev_t的存在。=核心改版可以放手修正,不會影響任何驅動程式3.2.3--副編號(dev_t&kdev_t)•可操作的kdev_t巨集與函式:–MAJOR(kdev_tdev)=從kdev結構取得主編號–MINOR(kdev_tdev)=從kdev結構取得次編號–MKDEV(intmajor,intminor)=以指定的主編號與次編號建立一個kdev_t結構–kdev_t_to_nr(kdev_tdev)=將一個kdev_t結構轉換成一個數值(dev_t)–to_kdev_t(intdev)=將一個數值轉換成kdev_t結構注意:由於核心模式並未定義dev_t,所以這裡用一個int來代替3.3—檔案作業•驅動程式內部以一個file結構來代表一個已開啟的裝置•核心透過一個file_operations結構來存取驅動程式內部的作業函式(method)都定義在linux/fs.hless/usr/src/linux-2.4.20/include/linux/fs.h•file結構包含一個f_op欄位,他是一個指向file_operations結構的指標•file_operation結構本身是由一系列“函式指標”所構成,這些指標分別指向驅動程式的各項作業函式。Ex:核心收到操作檔案的系統戶叫(ex:read()),依據系統呼叫指定的