Ch5-字元裝置驅(qū)動程式的進(jìn)階操作.ppt_第1頁
Ch5-字元裝置驅(qū)動程式的進(jìn)階操作.ppt_第2頁
Ch5-字元裝置驅(qū)動程式的進(jìn)階操作.ppt_第3頁
Ch5-字元裝置驅(qū)動程式的進(jìn)階操作.ppt_第4頁
Ch5-字元裝置驅(qū)動程式的進(jìn)階操作.ppt_第5頁
已閱讀5頁,還剩40頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

Ch5-字元裝置驅(qū)動程式的進(jìn)階操作,Outline,Introduction 5.1 ioctl 5.2 推延式I/O 5.3 poll與select 5.4 非同步通知 5.5 改變裝置的存取點 5.6 裝置檔的存取控制 5.7 回溯相容性 5.8 速查參考,5-Introduction,驅(qū)動程式除了讀寫動作之外,通常還需要提供各種控制硬體的能力,而控制動作通常是透過ioctl作業(yè)方法來實施。 不過,並非所有的裝置都採用ioctl控制方式,有的驅(qū)動程式採用了另一種控制技術(shù)(預(yù)先定義一組特殊序列來當(dāng)成控制命令),例如:tty ioctl( )系統(tǒng)呼叫為驅(qū)動程式提供了一個下達(dá)“裝置特有的命令(device-specific command)”的管道。這類命令的定義與功能是隨硬體裝置而定: 設(shè)定暫存器的狀態(tài) 進(jìn)入或離開某作業(yè)某作業(yè)模式 ioctl( )的作用:控制I/O通道,5.1-ioctl,User-space的觀點來看,ioctl( )系統(tǒng)呼叫的函式原形如下: int ioctl (int fd, int cmd, ) fd : file descriptor,檔案描述單元 cmd : 控制命令 : 並非代表不定量引數(shù),而是一個可有可無的引數(shù)-習(xí)慣上表示為 char *argp(為了通過編譯時期的型別檢查type checking) ioctl作業(yè)方法會收到下列宣告的引數(shù): int (*ioctl) (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) inode指標(biāo)指向?qū)?yīng)到應(yīng)用程式傳來的fd filp指標(biāo)指向一個代表裝置節(jié)點的file結(jié)構(gòu) cmd的值等於ioctl( )系統(tǒng)呼叫的第二引數(shù) 如果應(yīng)用程式發(fā)出的ioctl( )系統(tǒng)呼叫有第三個引數(shù),則ioctl作業(yè)方法會收到一個unsigned long型別的arg引數(shù),否則無意義,5.1-ioctl,由於編譯器無法檢查而外引數(shù)的型別,因此,當(dāng)應(yīng)用程式傳遞了一個無效引數(shù)給ioctl( ),驅(qū)動程式也要到執(zhí)行期才會知道錯誤。這項缺點是ioctl系統(tǒng)定義使然,但卻是ioctl( )為了提供通用功能性的必然代價。 大部份驅(qū)動程式實作出來的ioctl作業(yè)方法都包含了一個switch敘述,並依據(jù)cmd引數(shù)來選擇正確的處理程序。 不同的命令(cmd)有不同的代表值,通常在標(biāo)頭檔裡定義一組符號來代表各個命令值,5.1.1-選擇ioctl命令編號,編寫ioctl的具體程式之前,必須先為各個命令挑選對應(yīng)的編號。最簡單的選擇“從1開始逐一分配”是行不通的。 系統(tǒng)上,每個命令的編號都必須是獨一無二的 : 以免正確命令被下達(dá)到錯誤裝置所造成的災(zāi)難。 若沒有重複的ioctl命令編號,則搞錯對象的程式就會收到EINVAL錯誤,不至於。 初版的linux採用16bits的編號 : 高八位元代表裝置個體的“魔數(shù)(magic number)” 低八位元則是供裝置內(nèi)部使用的“序號(sequence number)” 同系統(tǒng)上,沒有相同魔數(shù)的裝置。同一裝置裡沒有重複的序號。,#define SCULL_IOCTL1 0x6b01 #define SCULL_IOCTL2 0x6b02,5.1.1-選擇ioctl命令編號,新版的劃分法則: 查閱include/asm/ioctl.h(定義所要使用的各個位元欄,包括:類型, 魔數(shù), 流水號, 傳輸方向) 查閱Documentation/ioctl-number.txt(列出所有已經(jīng)分配給核心的魔術(shù),及解釋了為何應(yīng)該採用新法則) 新的劃分法使用四個位元欄位 type(magic number) : 自己挑選一個符合規(guī)定的數(shù)值,並用於整個驅(qū)動程式。欄位長度為_IOC_TYPEBITS(8-bits) number(ordinal number) : 可稱為序號,此欄位的長度為_IOC_NRBITS(8-bits) direction : 傳輸方向,代表資料的流向。包括(_IOC_NONE, _IOC_READ, _IOC_WRITE, _IOC_READ | _IOC_WRITE)。英站在應(yīng)用程式的觀點來看。 size : 使用者資料量。此欄位的寬度隨硬體平臺而定。(8-bits14-bits)建議在8-bits以下來保持可移值性。,5.1.1-選擇ioctl命令編號,整數(shù)引數(shù)的傳遞方式有兩種,一是透過指標(biāo),二是直接給明確數(shù)值;ioctl( )的普遍慣例,應(yīng)該採用指標(biāo)來交換數(shù)值。 對於系統(tǒng)呼叫的回傳值有不成文的慣例:負(fù)值代表錯誤且被用來設(shè)定user-space的errno變數(shù),正值的意義由系統(tǒng)呼叫自己決定。 連動atomic(在實務(wù)上,驅(qū)動程式偶而會需要一口氣完成原本是分離的兩項動作,尤其是應(yīng)用程式需要設(shè)定, 或釋放lock時): X(交換)=G(取得)+S(設(shè)定) H(移位)=T(通知)+Q(查詢),/ * S means “Set” through a ptr,(設(shè)定) * T means “Tell” directly with the argument value(通知) * G means “Get”: reply by setting through a pointer(取得) * Q means “Query”: response is on the return value(查詢) * X means “eXchange”: G and S atomically(交換) * H means “sHift”: T and Q atomically(移位) */,#define SCULL_IOC_MAGIC k #define SCULL_IOCRESET _IO(SCULL_IOC_MAGIC, 0) /* Use k as magic number */ #define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC, 1, scull_quantum) #define SCULL_IOCSQSET _IOW(SCULL_IOC_MAGIC, 2, scull_qset) #define SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC, 3) #define SCULL_IOCTQSET _IO(SCULL_IOC_MAGIC, 4) #define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC, 5, scull_quantum) #define SCULL_IOCGQSET _IOR(SCULL_IOC_MAGIC, 6, scull_qset) #define SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC, 7) #define SCULL_IOCQQSET _IO(SCULL_IOC_MAGIC, 8) #define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9, scull_quantum) #define SCULL_IOCXQSET _IOWR(SCULL_IOC_MAGIC,10, scull_qset) #define SCULL_IOCHQUANTUM _IO(SCULL_IOC_MAGIC, 11) #define SCULL_IOCHQSET _IO(SCULL_IOC_MAGIC, 12) /*HARDRESET命令,可將模組的用量計次歸零*/,5.1.2-ioctl的回傳值,ioctl作業(yè)方法的具體內(nèi)容,主要是用來分辨命令編號(cmd引數(shù))的switch敘述。 如果cmd不符合任何命令編號,則default應(yīng)做? 許多核心合適採取的行為是回傳-EINVAL(Invalid Argument),這是合理的。 但,POSIX標(biāo)準(zhǔn)卻規(guī)定回傳-ENOTTY(Not a typewriter)這不甚合理,但libc6已將訊息改成比較合理的“Inappropriate ioctl for device”,5.1.3-預(yù)先定義的ioctl命令,雖然ioctl( )系統(tǒng)呼叫的主要作用對象是硬體裝置,但是核心本身仍能辨認(rèn)少數(shù)幾個命令(預(yù)設(shè)命令)。因此,當(dāng)你挑選的ioctl命令編號剛好與預(yù)定命令相同,則你寫出來的ioctl作業(yè)方法將永遠(yuǎn)收不到該命令,而應(yīng)用程式也會因為發(fā)出衝突的ioctl命令而遭遇到意外。 預(yù)定命令分為三大類: 可作用於任何檔案(正常檔, 裝置檔, FTFO或socket)的命令 只對正常檔案有作用的命令 只能用於特定檔案系統(tǒng)類型的命令 驅(qū)動程式設(shè)計者只須注意第一類命令(其魔數(shù)是“T”),5.1.3-預(yù)先定義的ioctl命令,以下是核心內(nèi)建的ioctl命令,可作用於任何檔案: FIOCLEX:設(shè)立close-on-exec旗標(biāo)。當(dāng)某行程開始執(zhí)行(使用exec系統(tǒng)呼叫) FIONCLEX:撤銷close-on-exec旗標(biāo) FIOASYNC:設(shè)立或撤銷檔案的“非同步通知” FIONBIO:”File IOctl Nonblock I/O”此命令會修改filp-f_flags裡的O_NONBBOCK旗標(biāo)。要下達(dá)此命令的應(yīng)用程式,必須在ioctl( )的第三引數(shù)註明他到底想要執(zhí)行“設(shè)立”或“撤銷”的動作。 fcntl( )系統(tǒng)呼叫也會修改O_NONBBOCK旗標(biāo)狀態(tài)。fcntl( )與ioctl( )非常類似,這兩者會被分開主要是基於歷史因素,當(dāng)初UNIX研發(fā)者再面對I/O控制作業(yè)的問題十,決定將“檔案”與“裝置”視為不同的東西,在當(dāng)時唯一具有ioctl作業(yè)方法的裝置只有裝端機(jī)(tty)。這解釋了為何ioctl( )沒收到正確命令時,傳回值竟是-ENOTTY。,5.1.4-ioctl的額外引數(shù)之用法,在開始研究scull如何實作ioctl作業(yè)方法之前,有必要先搞清楚如何使用它的額外引數(shù)(第三引數(shù))。 若該引數(shù)為整數(shù)時,那就直接拿來用。 如果是指標(biāo),就必須多費(fèi)點心。 若指標(biāo)指向user-space的位址,必須先確定該位址是有效的,而且其對應(yīng)的記憶頁目前已經(jīng)映射(mapped)到系統(tǒng)記憶體(RAM)。若核心程式試圖存取一個超出範(fàn)圍的位址,處理器會主動觸發(fā)一次異常(exception)。 2.2版之後,核驗位址合法性的工作,交由access_ok( )函式來進(jìn)行,此函式定義在。在2.2版前使用者必須自行核驗。 access_ok( )回傳值:1代表成功(可存取),0代表失敗(不能存取)。,5.1.4-ioctl的額外引數(shù)之用法,access_ok( )函式: type必須是VERIFY_READ或VERIFY_WRITE的其中之一(取決於想對user-space進(jìn)行的動作是讀入或?qū)懗? addr引數(shù)是要檢查的user-space位址 size是檢查範(fàn)圍(以byte為計算單位) access_ok( )值得注意的地方: access_ok( )並非徹底檢驗指定範(fàn)圍內(nèi)的每一個位址,而是檢驗受查記憶區(qū)是否在行程的合理存取範(fàn)圍(不屬kernel-space) 大部份的驅(qū)動程式並不需要刻意呼叫access_ok( )(記憶體存取程序會幫忙處理),int access_ok (int type, const void *addr, unsigned long size);,在呼叫access_ok( )之後,驅(qū)動程式就可放心進(jìn)行實際的傳輸。除了使用copy_from_user以及copy_to_user函式之外,還提供了一組針對常用資料規(guī)格而設(shè)計的傳輸工具: 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來看 * 所以“read”和“write”剛好相否 */ if (_IOC_DIR(cmd) ,5.1.5-機(jī)能管制,使用者能否存取裝置,需借助作業(yè)系統(tǒng)的權(quán)限控管機(jī)制(限制對象為人)。 對於限制對象是操作項目而言,驅(qū)動程式自己必須作一些而外檢查判斷使用者是否有權(quán)操作要求機(jī)能。 Ex:任何人的能讀寫磁帶機(jī),但並非人人都有權(quán)改變磁帶區(qū)塊的預(yù)設(shè)大小。 能力分權(quán)(capabilities),不在只分成“特權(quán)”與“非特權(quán)”,而細(xì)分成更多類細(xì)目: 可將某種機(jī)能的使用權(quán)開放給某特定程式,而不必將無關(guān)的其他權(quán)力也一並交出。 在user-space下分權(quán)觀念還不是很廣泛,但是在核心內(nèi)部則是高度依賴。,5.1.5-機(jī)能管制,機(jī)能分類,紀(jì)錄在 CAP_DAC_OVERRIDE:改變檔案或目錄之存取權(quán)限的能力。 CAP_NET_ADMIN:執(zhí)行網(wǎng)路控管工作的能力(包括會影響網(wǎng)路介面的動作)。 CAP_SYS_MODULE:將模組載入, 移出核心的能力。 CAP_SYS_RAWIO:執(zhí)行“原始I/O(raw I/O)”作業(yè)能力。Ex:存取裝置的I/O port,直接與USB裝置通訊。 CAP_SYS_ADMIN:一種無所不能的能力,提供系統(tǒng)管理作業(yè)所需的一切存取能力。 CAP_SYS_TTY_CONFIG:設(shè)定tty組態(tài)的能力。 驅(qū)動程式應(yīng)先以capable( )函式檢查calling process是否具備適當(dāng)?shù)哪芰?int capable (int capability);,5.1.6-命令的實作,Scull的ioctl作業(yè)方法,指示傳輸了幾個可調(diào)整的參數(shù)而以,如下:,#ifdef SCULL_DEBUG case SCULL_IOCHARDRESET: /* 將用量計次設(shè)定為1,以便在出錯時仍然可以謝載模組 /* 使用1而非0,是因為發(fā)出的ioctl的行程在佔用此裝置 /* 在該行程關(guān)閉此裝置時,壯量計次會歸零 while (MOD_IN_USE) MOD_DEC_USE_COUNT; MOD_INC_USE_COUNT; /* 不使用break,使其落入後續(xù)的條件判斷 #endif /* SCULL_DEBUG */ 略,5.1.6-命令的實作,一般驅(qū)動程式不會在同一地方混合使用這麼多種呼叫模式,scull只是為了展示所有可能的做法。 不過資料交換的功能通常是必要的,不果是透過指標(biāo)(常見)或是直接傳值(少見)都可以,但應(yīng)該要極力避免混用兩種技術(shù)(指標(biāo)+傳值),5.1.7-不需要ioctl的裝置控制法,這種控制方式稱為“命令導(dǎo)向式控制法(command-oriented)”: 優(yōu)點:簡便,使用者只要寫入特殊資料(命令)就能控制裝置,而不需要使用額外的工具程式(ex : tty驅(qū)動程式, ) 缺點:在操作策略上有所限制,容易造成驅(qū)動程式將一般資料當(dāng)成控制命令來處理(“控制命令”與“平常資料”共用同一個傳輸管道) 對於不會傳輸資料,只會對命令作出回應(yīng)的裝置(例如:機(jī)械手臂),命令導(dǎo)向式控制法就肯定是最理想的選擇。 如果目標(biāo)裝置適合使用命令導(dǎo)向式的控制法,驅(qū)動程式自然不必提供ioctl,而是一段能解讀控制命令的程式(interpreter)。,5.2-Blocking I/O,read( )可能遭遇的狀況“資料還沒結(jié)束(到達(dá)EOF),只是尚未到達(dá)而已”? 答案是,先去睡一覺等資料到期在說。 本節(jié)宗旨: 如何對行程進(jìn)行催眠? 如何喚醒行程? 如何能夠事先主動查詢資料是否存在,而非盲目的發(fā)出read( )系統(tǒng)呼叫之後叫不自覺的睡著. 同樣的觀念也應(yīng)用到write( )上,5.2.1-Going to Sleep and Awakening,Linux提供了多種處理催眠與喚醒的做法,方法雖然不同,但是所需的基本資料型別卻是相同的:一個待命佇列(wait queue),即wait_queue_head_t my_queue(一個待命佇列紀(jì)錄了正在等待同一事件的行程)。 以靜態(tài)方式宣告的待命佇列可以在編譯期就予以初始化: 忘了對待命佇列初始化是常見錯誤,通常會導(dǎo)致意料外的後果。,wait_queue_head_t my_queue; /*待命佇列的宣告 init_waitqueue_head ( /*初始化程序,DECLARE_WAIT_QUEUE_HEAD (my_queue);,5.2.1-Going to Sleep and Awakening,在待命佇列被宣告與初始化之後,就可用來安置休眠行程。將行程推入休眠狀態(tài)(催眠),世界由呼叫sleep_on( )的各種變體函式之一來達(dá)成的,要使用哪一種變體,要看希望休眠程度有多“沉”: sleep_on(wait_queue_head_t *queue); zombie殭屍 interruptible_sleep_on(wait_queue_head_t *queue); sleep_on_timeout(wait_queue_head_t *queue, long timeout); interruptible_sleep_on(wait_queue_head_t *queue, long timeout); void wait_event(wait_queue_head_t queue, int condition); int wait_event_interruptible(wait_queue_head_t queue, int condition); 結(jié)合了“等待事件發(fā)生”與一個“指定測試條件”為甦醒條件,藉此避免發(fā)生相競情況。也就是說,休眠狀態(tài)會持續(xù)蹈事件發(fā)生,或是condition所代表的條件成立為止。這兩個巨集展開後都會行程do-while迴圈,而回圈每一回合都會重新估算condition的值,5.2.1-Going to Sleep and Awakening,休眠只是問題的一半,在某處一定還要有某樣?xùn)|西於未來的某時間點喚醒行程。一般來說,驅(qū)動程式通常是在interrupt handler收到新資料時,才喚醒其休眠行程。 如同催眠,喚醒的方式也不只一種手法,核心提供的高階喚醒函式如下: wake_up(wait_queue_head_t *queue); 喚醒在queue裡所有行程 wake_up_interruptible (wait_queue_head_t *queue); wake_up_sync (wait_queue_head_t *queue); wake_up_interruptible_sync (wait_queue_head_t *queue);正常行況下,呼叫一次wake_up( )會立刻導(dǎo)致一次重新排程(reschedule),這表示在wake_up( )返回之前,可能已有其他行程先開跑了。同步化的版本址是負(fù)責(zé)喚醒行程(進(jìn)入runable狀態(tài)),而不呼叫排程器。,5.2.1-Going to Sleep and Awakening,範(fàn)例misc-modules/sleepy.c,rootzwai misc-modules# make rootzwai misc-modules# insmod ./sleepy.o Warning: loading ./sleepy.o will taint the kernel: no license rootzwai misc-modules# cat /proc/devices |grep sleepy 253 sleepy rootzwai misc-modules# mknod /dev/sleepy c 253 0 1+ Exit 1 cat /dev/sleep rootzwai misc-modules# cat /dev/sleepy & 1 26770 rootzwai misc-modules# echo trash /dev/sleepy,5.2.2-A Deeper Look at Wait Queues,待命佇列(wait queue)是什麼? wait_queue_head_t型別(定義於)是一個相當(dāng)簡單的結(jié)構(gòu),只包含一個lock變數(shù),以及一個紀(jì)錄休眠行程的鏈結(jié)串列。 通常,wait_queue_t結(jié)構(gòu)是interruptible_sleep_on( )之類的函式自己從堆疊配置而來。,wait_queue_head_t,spinlock_t lock;,struct list_head task_list;,wait_queue_t,struct task_struct *task;,struct list_head task_list;,佇列中沒有休眠的行程,wait_queue結(jié)構(gòu)本身,目前行程與其堆疊頁,另一行程與其堆疊頁,Wait Queues in Linux 2.4,自備wait_queue_head_t的裝置結(jié)構(gòu),目前的行程正在裝置的佇列裡休眠,多個行程正在同一個佇列裡休眠,5.2.2-A Deeper Look at Wait Queues,建立新的wait_queue_t變數(shù)(wait,來自堆疊)並設(shè)定初值。 設(shè)定行程的狀態(tài)為TASK_INTERRUPTIBLE(隨時可岔斷) 將待命佇列項目加到佇列(wait_queue_head_t* 引數(shù)) 呼叫schedule( ),把處理器的使用權(quán)讓出,void simplified_sleep_on(wait_queue_head_t *queue) wait_queue_t wait; init_waitqueue_entry( ,5.2.2-A Deeper Look at Wait Queues,刻意呼叫schedule( )的原因,是為了進(jìn)行專程等待(exclusive wait)。多個行程等待同一事件時,當(dāng)wake_up( )被呼叫時,它是喚醒所有正在等待該事件的行程。如果要等待的事件,是收到一段必須獨占存取的資料,所以只有一個行程能順利爭取該段資料,而其餘剛才被喚醒的行程,則因為沒有資料可讀,又回復(fù)到休眠狀態(tài)。這種現(xiàn)在被稱為“驚蟄問題(thundering herd problem)”在講究高效率的環(huán)境,驚蟄問題會浪費(fèi)掉大量的系統(tǒng)資源。,5.2.2-A Deeper Look at Wait Queues,在2.3版研發(fā)過程中,開發(fā)人員提出了專程修眠(exclusive sleep)的觀念。對於需要競逐獨占資源的行程,在休眠之前,事先告訴核心,當(dāng)獨占資源釋出時,一次只叫醒其中之一就夠了。,void simplified_sleep_exclusive(wait_queue_head_t *queue) wait_queue_t wait; init_waitqueue_entry( ,5.2.3-Writing Reentrant Code,當(dāng)行程進(jìn)入休眠狀態(tài),驅(qū)動程式本身其實還活著,可被另一個行程叫用。(例,當(dāng)一個應(yīng)用程式再tty1等待鍵盤輸入時,使用者切換到tty2並開啟新的shell。這兩個shells都是透過同個驅(qū)動程式來等待鍵盤輸入,但是卻分別睡在不同的待命佇列裡) 一段程式碼若要能被安全同時執(zhí)行,必須符合一項原則:不得使用全域變數(shù)來儲存狀態(tài)資訊。 符合上述條件的程式,被形容為可重返的(rentrantable),只要每個行程都有自己專屬的狀態(tài)資訊,就不會發(fā)生互相干擾的問題。 符合1.會呼叫schedule( )或2.需要與user-space交換資料的函式都必須遵守可重返的原則。,5.2.4-Blocking and Nonblocking Operations,filp-f_flags的O-NONBLOCK角色。此旗標(biāo)預(yù)設(shè)值是0,行程再等待資料十,一班都會進(jìn)入休眠狀態(tài)。 對於推延式操作,作業(yè)方法必須實踐下列行為: 當(dāng)read被啟動,但是資料尚未抵達(dá),則必須推延行程。資料抵達(dá)時必須立刻喚醒行程(不管齊全與否)。 當(dāng)write被啟動,但是緩衝區(qū)已沒有空間,則必須推延行程,而且必須將修眠行程放在不同於read所用的待命佇列。若輸出緩衝區(qū)挪出空間,就立刻喚醒行程,並讓write成功返環(huán),即使址寫入部分資料。 對於非推延操作,read與write的行為模式可就大不相同。 當(dāng)行程觸發(fā)read( )或write( )時,若驅(qū)動程式無法即時提供資料,或是輸出緩衝區(qū)沒有足夠空間,必須立刻傳回-EAGAIN。 注意,只有read, write與open才會受到O_NONBLOCK旗標(biāo)影響。,5.2.5-A Sample Implementation:scullpipe,Scull沒有和任何硬體有關(guān)係,因此需要使用另一個行程來產(chǎn)生資料。 scullpipe驅(qū)動程式所需要的裝置結(jié)構(gòu),必須要有兩個待命佇列,以及一個緩衝區(qū)。緩衝區(qū)的容量可讓使用者自行調(diào)整(在編譯期, 載入期或執(zhí)行期) 。 less pipe.c 重要觀念:有兩種狀況可能會使得行程被迫進(jìn)入休眠 1.當(dāng)行程呼叫schedule( )(不論直接或間接) 2.與user-space交換資料時(目標(biāo)資料可能剛好不在主記憶體,必須等待從swap-space換回來) Write實作方式相當(dāng)類似於read,為一“特殊”之處在於絕對不會完全填滿緩衝區(qū),至少會留下一個byte的空洞。,5.3-poll and select,nonblocking I/O應(yīng)用程式,通常會使用select( )與poll( )系統(tǒng)呼叫=讓行程判斷下次的I/O動作會部會推延。 select( )是BSD Unix提出的構(gòu)想 poll( )是System V的解決方案 當(dāng)應(yīng)用程式對裝置檔發(fā)出poll( )或select( )系統(tǒng)呼叫,就會觸發(fā)該裝置檔驅(qū)動程式的poll作業(yè)方法,而此作業(yè)方法必須執(zhí)行以下兩個步驟: 將可能影響輪詢狀態(tài)的任何待命佇列傳給poll_wait( ) 傳回一個位元遮罩,描述些操作項目可立即執(zhí)行而不會推延 為何不統(tǒng)一由核心自己執(zhí)行? 關(guān)鍵在輪詢狀態(tài)資訊只有驅(qū)動程式自己才能提供,核心無法事先得知。,unsigned int (*poll) (struct file *, poll_table *);,5.3-poll and select,poll_table結(jié)構(gòu) 宣告在 每個驅(qū)動程式都應(yīng)該引入此標(biāo)頭檔 poll.h預(yù)先定義了一系列常數(shù)的各種可能的輪詢狀態(tài) POLLIN - POLLOUT POLLRDNORM - POLLWRNORM POLLRDBAND - POLLWRBAND POLLPRI, POLLHUP, POLLER,void poll_wait(struct file *, wait_queue_head_t *, poll_table *);,5.3.1-Interaction with read and write,poll( )與select( )最重要的使命=讓應(yīng)用程式同時等待多個資料串流 從裝置讀出資料(Read) 如果資料已經(jīng)在輸入緩衝區(qū)裡 如果輸入緩衝區(qū)是空的 如果遇到檔尾(EOF) 從裝置寫入資料(Write) 如果緩衝區(qū)上有空間 如果緩衝區(qū)是滿的 絕對不要讓write( )系統(tǒng)呼叫等待資料傳輸(即使沒設(shè)立O_NONBLOCK) 通則,適當(dāng)?shù)囊勒彰糠N裝置修改某些規(guī)則是容許的,甚至是必要的。,5.3.1-Interaction with Read and Write,出清延宕資料(POLL) 光靠write作業(yè)方法本身,並不能滿足“徹底輸出所有資料”的需求。以fsync作業(yè)方法來彌補(bǔ)此一缺憾。 驅(qū)動程式只要有部分應(yīng)用程式需要確認(rèn)資料有否如實寫出到裝置上,驅(qū)動程式本身就必須提供fsync作業(yè)方法。 不管當(dāng)初是否以O(shè)_NONBLOCK模式開啟裝置,在發(fā)出fsync( )系統(tǒng)呼叫之後,等到返回之時,就可認(rèn)定先前用write( )寫出的資料,已經(jīng)全數(shù)出清(fiush)到裝置上。 Fsync作業(yè)方法沒有不尋常的特性,呼叫者擺明了就是願意等待。一般而言,char drive:將fops指標(biāo)指向NULL。而block driver:以通用的block_fsync來完成任務(wù)。,5.3.2-The Underlying Data Structure,poll_table結(jié)構(gòu)的作用: 當(dāng)應(yīng)用程式觸發(fā)poll( )或select的其中之一,核心就依據(jù)系統(tǒng)呼叫指定的fd來啟動對應(yīng)裝置檔的poll作業(yè)方法,並將相同的poll_table回傳。 poll_table本身是一個由poll_table_entry結(jié)構(gòu)所組成的陣列,每個poll_table_entry結(jié)構(gòu)都是核心在收到poll( )或select( )系統(tǒng)呼叫時,特地為該次呼叫所配置的。 如果被調(diào)查的驅(qū)動程式中,沒有任何一個表示能立即執(zhí)行I/O而不推延,則poll( )會休眠,直到它所處的待命佇列之一甦醒為止。 在poll( )系統(tǒng)呼叫完成任務(wù)之後,poll_table結(jié)構(gòu)就被釋放掉了,而先前被加到poll_table的所有待命佇列項目也會被移出輪詢表。 及fs/select.c,struct poll_table_struct,int error;,struct poll_table_page *tables;,struct poll_table_entry,wait_queue_t wait;,wait_queue_head_t *wait_address;,只調(diào)查一個裝置,Poll 所用的資料結(jié)構(gòu),自備wait_queue_head_t的一般裝置結(jié)構(gòu),正在輪詢兩個裝置,一個觸發(fā)了poll()系統(tǒng)呼叫的行程,poll_table_struct結(jié)構(gòu),輪詢表項目,5.4-Asynchronous Notification,blocking/nonblocking操作與poll作業(yè)方法的組合,通常已經(jīng)足以應(yīng)付狀態(tài)查詢的需求,但仍有些情況以目前所知道技術(shù)還不足以有效解決。 非同步通知,應(yīng)用程式必須執(zhí)行兩個步驟,才能得到輸入檔的非同步通知: 1.指定一個行程當(dāng)輸入檔的“擁有者”當(dāng)行程使用fcntl( )系統(tǒng)呼叫發(fā)出F_SETOWN命令時,擁有者的PID便會被存入filp-f_owner,以便往後使用。=讓核心知道通知對象是誰。 2.使用fcntl( )系統(tǒng)呼叫發(fā)出F_SETFL命令,設(shè)定裝置的FASYNC旗標(biāo)。

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論