版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、Linux 設(shè)備驅(qū)動(dòng)程序?qū)W習(xí)(5 )-高級(jí)字符驅(qū)動(dòng)程序操作(2 )阻塞型I/O和休眠Linux設(shè)備驅(qū)動(dòng)程序?qū)W習(xí)(5)-高級(jí)字符驅(qū)動(dòng)程序操作(2 )阻塞型I/O和休眠這一部分主要討論:如果驅(qū)動(dòng)程序無(wú)法立即滿足請(qǐng)求,該如何響應(yīng)?(65865346 ),、休眠進(jìn)程被置為休眠,意味著它被標(biāo)識(shí)為處于一個(gè)特殊的狀態(tài)并且從調(diào)度器的運(yùn)行隊(duì)列中移走。這個(gè)進(jìn)程將不被在任何 CPU上調(diào)度,即將不會(huì)運(yùn)行。直到發(fā)生某些事情改變了那個(gè)狀態(tài)。安全地進(jìn)入休眠的兩條規(guī)則:(1)永遠(yuǎn)不要在原子上下文中進(jìn)入休眠,即 當(dāng)驅(qū)動(dòng)在持有一個(gè)自旋鎖、seqlock或者RCU鎖時(shí)不能睡眠;關(guān)閉中斷也不能睡眠。持有一個(gè)信號(hào)量時(shí)休眠是合法的,但你
2、應(yīng)當(dāng)仔細(xì)查看代碼:如果代碼在持有一個(gè)信號(hào)量時(shí)睡眠,任何其他的等待這個(gè)信號(hào)量的線程也會(huì)休眠。因此發(fā)生在持有信號(hào)量時(shí)的休眠必須短暫, 而且決不能阻塞那個(gè)將最終喚醒你的進(jìn)程。(2)當(dāng)進(jìn)程被喚醒,它并不知道休眠了多長(zhǎng)時(shí)間以及休眠時(shí)發(fā)生什么;也不知道是否另有進(jìn)程也在休眠等待同一事件,且那個(gè)進(jìn)程可能在它之前醒來(lái)并獲取了所等待的資源。所以不能對(duì)喚醒后的系統(tǒng)狀態(tài)做任何的假設(shè),并必須重新檢查等待條件來(lái)確保正確的響應(yīng)。除非確信其他進(jìn)程會(huì)在其他地方喚醒休眠的進(jìn)程,否則也不能睡眠。使進(jìn)程可被 找到意味著:需要維護(hù)一個(gè)稱為等待隊(duì)列的數(shù)據(jù)結(jié)構(gòu)。它是一個(gè)進(jìn)程鏈表,其中飽含了等待某個(gè)特定事件的所有進(jìn)程。在Linux中,一個(gè)等
3、待隊(duì)列由一個(gè)wait_queue_head_t結(jié)構(gòu)體來(lái)管理,其定義在 <linux/wait.h>中。wait_queue_head_t 類型的數(shù)據(jù)結(jié)構(gòu)非常簡(jiǎn)單:struct _wait_queue_head spinl ock_t lock ;struct list_head task_list ;typedef struct _wait_queue_head wait_queue_head_t它包含一個(gè)自旋鎖和一個(gè)鏈表。 這個(gè)鏈表是一個(gè)等待隊(duì)列入口,它被聲明做 wait_queue_t 。wait_queue_head_t包含關(guān)于睡眠進(jìn)程的信息和它想怎樣被喚醒.簡(jiǎn)單休眠(其實(shí)是
4、高級(jí)休眠的宏)Linux內(nèi)核中最簡(jiǎn)單的休眠方式是稱為wait_event的宏(及其變種),它實(shí)現(xiàn)了休眠和進(jìn)程等待的條件的檢查。形式如下:wait_event (queue, condition )/* 不可中斷休眠,不推薦*/wait_event_interruptible(queue, condition )/* 推薦,返回非零值意味著休眠被中斷,且驅(qū)動(dòng)應(yīng)返回-ERESTARTSYS*/wait_event_timeout (queue, condition , timeout )wait_event_interruptible_timeout(queue, condition , time
5、out )/*有限的時(shí)間的休眠;若超時(shí),則不管條件為何值 返回0,*/喚醒休眠進(jìn)程的函數(shù)稱為 wake_up ,形式如下:void wake_up( wait_queue_head_t * queue);void wake_up_interruptible(wait_queue_head_t *queue);慣例:用 wake_up 喚醒 wait_event ;用 wake_up_interruptible喚醒 wait_event_interruptible。簡(jiǎn)單休眠實(shí)驗(yàn)?zāi)K程序鏈接:模塊測(cè)試程序鏈接:sbepy test實(shí)驗(yàn)現(xiàn)象:Tekkaman244OSBC2440V4d / lib
6、/ modules/ Tekkaman244OSBC2440#Vnsmod sleepy . ko Tekkama n244OSBC2440V4d / dev/Tekkaman244OSBC2440#V4at / proc / devicesCharacter devices :1 mem2 pty3 ttyp4 / dev/ vc/ 04 tty4 ttyS5 / dev/ tty5 / dev/ con sole5 / dev/ ptmx7 vcs10 misc13 in put14 sound81 video4li nux89 i2c90 mtd116 alsa128 ptm136 pt
7、s180 usb189 usb_device204 s3c2410_serial252 sleepy253 usb_e ndpo int254 rtcBlock devices :1 ramdisk256 rfd7 loop31 mtdblock93 nftl96 inftl179 mmcTekkaman244OSBC2440V4nknod - m 666 sleepy c 252 0Tekkama n244OSBC2440V4d / tmp/Tekkaman244OSBC2440V4 sleepy_testr &Tekkaman244OSBC2440#V4 sleepy_testr
8、&Tekkama n244OSBC2440VpsPID Uid VSZ Stat Comma nd1 root 1744 S init2 root SW< kthreadd 3 root SWNksoftirqd / 04 root SW< watchdog/ 05 root SW< events / 06 root SW< khelper 59 root SW< kblockd / 060 root SW< ksuspend_usbd63 root SW< khubd65 root SW< kseriod 77 root SWpdflu
9、sh 78 root SWpdflush 79 root SW< kswapdq80 root SW< aio / 0707 root SW< mtdblockd 708 root SW< nftld 709 root SW< inftld710 root SW< rfdd 742 root SW < kpsmoused751 root SW < kmmcfl769 root SW < rpciod / 0778 root 1752 S - sh779 root 1744 S init781 root 1744 S init783 root
10、 1744 S init787 root 1744 S init799 root 1336 S ./sleepy_testr800 root 1336 S ./sleepy_testr802 root 1744 R psTekkaman244OSBC2440V4 sleepy_testw read code =0write code =02 + Done ./ sleepy_testrTekkama n244OSBC2440VpsPID Uid VSZ Stat Comma nd1 root 1744 S init2 root SW< kthreadd 3 root SWNksoftir
11、qd / 04 root SW< watchdog/ 05 root SW< events / 06 root SW< khelper 59 root SW< kblockd / 060 root SW< ksuspend_usbd63 root SW< khubd65 root SW< kseriod 77 root SWpdflush 78 root SWpdflush 79 root SW < kswapdq80 root SW < aio/ 0707 root SW < mtdblockd 708 root SW < n
12、ftld 709 root SW < inftld 710 root SW < rfdd 742 root SW < kpsmoused751 root SW < kmmcd769 root SW < rpciod / 0778 root 1752 S - sh779 root 1744 S init781 root 1744 S init783 root 1744 S init787 root 1744 S init799 root 1336 S ./sleepy_testr804 root 1744 R psTekkaman2440SBC2440V4 slee
13、py_testwwrite code =0Tekkama n2440SBC2440Vrfead code =01 + Done ./ sleepy_testr Tekkama n2440SBC2440VpsPID Uid VSZ Stat Comma nd1 root 1744 S init2 root SW < kthreadd 3 root SWN ksoftirqd / 04 root SW < watchdog/ 05 root SW < events / 0root SW < khelper 59 root SW60 root SW63 root SW65 r
14、oot SW77 root SW78 root SW79 root SW80 root SW707 root SW708 root SW709 root SW710 root SW742 root SW751 root SW769 root SW< kblockd / 0< ksuspend_usbd< khubd< kseriod pdflush pdflush < kswapdq< aio / 0< mtdblockd < nftld < inftld < rfdd < kpsmoused< kmmcd< rpc
15、iod / 0778 root 1752 S - sh779 root 1744 S init781 root 1744 S init783 root 1744 S init787 root 1744 S init806 root 1744 R ps阻塞和非阻塞操作全功能的read和write 方法涉及到進(jìn)程可以決定是進(jìn)行非阻塞I/O還是阻塞I/O操作。明確的非阻塞I/O由filp->f_flags 中的O_NONBLOCK 標(biāo)志來(lái)指示(定義再<linux/fcntl.h> ,被<linux/fs.h>自動(dòng)包含)。瀏覽源碼,會(huì)發(fā)現(xiàn)O_NON BLOCK的另一個(gè)名字
16、:O_N DELAY,這是為了兼容 SystemV代碼。O_NON BLOCK標(biāo)志缺省地被清除,因?yàn)榈却龜?shù)據(jù)的進(jìn)程的正常行為只是睡眠.其實(shí)不一定只有read和write方法有阻塞操作,open也可以有阻塞操作。后面會(huì)見(jiàn)到。而我的項(xiàng)目有一個(gè)和CPLD的接口的驅(qū)動(dòng),我決定要在ioctl中使用阻塞。while (dev->rp = dev -> wp»/* nothing to read */up (& dev-> sem); /* release the lock */if (filp ->f_flags & O_NONBLOCKreturn -
17、EAGAINPDEBUG (""%s" reading: going to sleep'n", current ->comrmif (wait_event_interruptible(dev->inq, (dev->rp != dev->wp»)return - ERESTARTSYS* signal: tell the fs layer to handle it */* otherwise loop, but first reacquire the lock */if (dow nn terruptible(&
18、amp; dev->sem)return - ERESTARTS;YS/* ok, data is there, retur n someth ing */咼級(jí)休眠步驟:(1) 分配和初始化一個(gè) wait_queue_t 結(jié)構(gòu),隨后將其添加到正確的等待隊(duì) 列。(2) 設(shè)置進(jìn)程狀態(tài),標(biāo)記為休眠。在<linux/sched.h>中定義有幾個(gè)任務(wù)狀態(tài):TASK_RUNNING意思是進(jìn)程能夠運(yùn)行。有 2個(gè)狀態(tài)指示一個(gè)進(jìn)程是在睡眠:TASK_INTERRUPTIBLE 和 TASK_UNTINTERRUPTIBLE 。2.6 內(nèi)核 的驅(qū)動(dòng)代碼通常不需要直接操作進(jìn)程狀態(tài)。但如果需要這樣做
19、使用的代碼是:void set_current_state(int new_state );在老的代碼中,你常常見(jiàn)到如此的東西:current->state = TASK_INTERRUPTIBLE;但是象這樣直接改變current是不推薦的,當(dāng)數(shù)據(jù)結(jié)構(gòu)改變時(shí)這樣的代碼將會(huì)失效。通過(guò)改變 current狀態(tài),只改變了調(diào)度器對(duì)待進(jìn)程的方式,但進(jìn)程還未讓出處理器。(3) 最后一步是放棄處理器。但必須先檢查進(jìn)入休眠的條件。如果不做檢查會(huì)引入競(jìng)態(tài):如果在忙于上面的這個(gè)過(guò)程時(shí)有其他的線程剛剛試圖喚醒你,你可能錯(cuò)過(guò)喚醒且長(zhǎng)時(shí)間休眠。因此典型的代碼下:if (! condition )(2)當(dāng)wake
20、_up 被在一個(gè)等待隊(duì)列上調(diào)用,它在喚醒第一個(gè)有 WQ_FLAG_EXCLUSIVE 標(biāo)志的進(jìn)程后停止喚醒.但內(nèi)核仍然每次喚醒所有的非獨(dú)占等待。schedule ();如果代碼只是從schedule 返回,則進(jìn)程處于TASK_RUNNING 狀態(tài)。如果不需睡眠而跳過(guò)對(duì) schedule 的調(diào)用,必須將任務(wù)狀態(tài)重置為TASK_RUNNING,還必要從等待隊(duì)列中去除這個(gè)進(jìn)程,否則它可能被多次喚醒。手工休眠/*( 1)創(chuàng)建和初始化一個(gè)等待隊(duì)列。常由宏定義完成:*/DEFINE_WA(Tny_wait);/*name是等待隊(duì)列入口項(xiàng)的名字.也可以用2步來(lái)做:*/wait_queue_t my_wait
21、 ;ini t_wait(&m y_wait);/*常用的做法是放一個(gè)DEFINE_WAIT在循環(huán)的頂部,來(lái)實(shí)現(xiàn)休眠。*/*(2)添加等待隊(duì)列入口到隊(duì)列,并設(shè)置進(jìn)程狀態(tài):*/void prepare_to_wait (wait_queue_head_t * queue , wait_queue_t *wait , int state );/*queue 和wait分別地是等待隊(duì)列頭和進(jìn)程入口。 state 是進(jìn)程的新?tīng)顟B(tài):TASK_INTERRUPTIBLE( 可中斷休眠,推薦)或TASK_UNINTERRUPTIBLE( 不可中斷休眠,不推薦)。*/*(3)在檢查確認(rèn)仍然需要休眠之后
22、調(diào)用 schedule*/schedule ();/*( 4)schedule 返回,就到了清理時(shí)間:*/void finish_wait (wait_queue_head_t *queu e, wait_queue_t *wait );認(rèn)真地看簡(jiǎn)單休眠中的wait_eve nt (queue con diti on )和wait_eve nt_in terruptible(queue, con diti on )底層源碼會(huì)發(fā)現(xiàn),其實(shí)他們只是手工休眠中的函數(shù)的組合。所以怕麻煩的話還是用 wait event比較好。獨(dú)占等待當(dāng)一個(gè)進(jìn)程調(diào)用 wake_up 在等待隊(duì)列上,所有的在這個(gè)隊(duì)列上等待的進(jìn)
23、程被置為可運(yùn)行的。 這在許多情況下是正確的做法。但有時(shí),可能只有一個(gè)被喚醒的進(jìn)程將成功獲得需要的資源,而其余的將再次休眠。這時(shí)如果等待隊(duì)列中的進(jìn)程數(shù)目大,這可能嚴(yán)重降低系統(tǒng)性能。為此,內(nèi)核開(kāi)發(fā)者增加了一個(gè)獨(dú)占等待"選項(xiàng)。它與一個(gè)正常的睡眠有2個(gè)重要的不同:采用獨(dú)占等待要滿足2個(gè)條件:(1) 希望對(duì)資源進(jìn)行有效競(jìng)爭(zhēng);(2) 當(dāng)資源可用時(shí),喚醒一個(gè)進(jìn)程就足夠來(lái)完全消耗資源。使一個(gè)進(jìn)程進(jìn)入獨(dú)占等待,可調(diào)用:void prepare_to_wait_exclusive (wait_queue_head_t *queue, wait_queue_t *wait , int state );注意
24、:無(wú)法使用 wait_eve nt和它的變體來(lái)進(jìn)行獨(dú)占等待喚醒的相關(guān)函數(shù)很少會(huì)需要調(diào)用wake_up_interruptible之外的喚醒函數(shù),但為完整起見(jiàn),這里是整個(gè)集合:wake_up( wait_queue_head_t *queue);wake_up_interruptible (wait_queue_head_t * queue);/*wake_up喚醒隊(duì)列中的每個(gè)非獨(dú)占等待進(jìn)程和一個(gè)獨(dú)占等待進(jìn)程。 wake_up_interruptible 同樣,除了它跳過(guò)處于不可中斷休眠的進(jìn)程。它 們?cè)诜祷刂?,使一個(gè)或多個(gè)進(jìn)程被喚醒、被調(diào)度(如果它們被從一個(gè)原子 上下文調(diào)用,這就不會(huì)發(fā)生).*
25、/wake_up_nr( wait_queue_head_t * queue, int nr );wake_up_interruptible_nr(wait_queue_head_t *queue, int nr );/*這些函數(shù)類似wake_up,除了它們能夠喚醒多達(dá) nr個(gè)獨(dú)占等待者,而 不只是一個(gè).注意傳遞0被解釋為請(qǐng)求所有的互斥等待者都被喚醒*/wake_up_all (wait_queue_head_t *queue); wake_up_interruptible_all(wait_queue_head_t * queue);/*這種wake_up喚醒所有的進(jìn)程,不管它們是否進(jìn)行獨(dú)占
26、等待(可中斷的 類型仍然跳過(guò)在做不可中斷等待的進(jìn)程)*/wake_up_interruptible_sync(wait_queue_head_t * queue);/* 一個(gè)被喚醒的進(jìn)程可能搶占當(dāng)前進(jìn)程,并且在wake_up返回之前被調(diào)度 到處理器。但是,如果你需要不要被調(diào)度出處理器時(shí),可以使用wake_up_interruptible的"同步"變體.這個(gè)函數(shù)最常用在調(diào)用者首先要完成剩下的少量工作,且不希望被調(diào)度出處理器時(shí)。*/poll禾口select當(dāng)應(yīng)用程序需要進(jìn)行對(duì)多文件讀寫(xiě)時(shí),若某個(gè)文件沒(méi)有準(zhǔn)備好,則系統(tǒng)會(huì)處于讀寫(xiě)阻塞的狀態(tài),并影響了其他文件的讀寫(xiě)。為了避免這種情況
27、,在必須使用多輸入輸出流又不想阻塞在它們?nèi)魏我粋€(gè)上的應(yīng)用程序常將非阻塞I/O和poll ( System V )、select ( BSD Unix )、 epoll(linux2.5.45開(kāi)始)系統(tǒng)調(diào)用配合使用。當(dāng)poll函數(shù)返回時(shí),會(huì)給出一個(gè)文件是否可讀寫(xiě)的標(biāo)志,應(yīng)用程序根據(jù)不同的標(biāo)志讀寫(xiě)相應(yīng)的文件,實(shí)現(xiàn)非阻塞的讀寫(xiě)。這些系統(tǒng)調(diào)用功能相同:允許進(jìn)程來(lái)決定它是否可讀或?qū)懸粋€(gè)或多個(gè)文件而不阻塞。這些調(diào)用也可阻塞進(jìn)程直到任何一個(gè)給定集合的文件描述符可用來(lái)讀或?qū)?。這些調(diào)用都需要來(lái)自設(shè)備驅(qū)動(dòng)中poll方法的支持,poll返回不同的標(biāo)志,告訴主進(jìn)程文件是否可以讀寫(xiě),其原型(定義在<linuxpo
28、ll.h> ):unsigned int (* poll ) (struct file *filp , poll_table *wait);實(shí)現(xiàn)這個(gè)設(shè)備方法分兩步:1.在一個(gè)或多個(gè)可指示查詢狀態(tài)變化的等待隊(duì)列上調(diào)用poll_wait.如果沒(méi)有文件描述符可用來(lái)執(zhí)行I/O,內(nèi)核使這個(gè)進(jìn)程在等待隊(duì)列上等待所有的傳遞給系統(tǒng)調(diào)用的文件描述符.驅(qū)動(dòng)通過(guò)調(diào)用函數(shù)poll_wait增加一個(gè)等待隊(duì)列到portable結(jié)構(gòu),原型:void poll_wait (struct file *, wait_queue_head_t *, poll_table *);2.返回一個(gè)位掩碼:描述可能不必阻塞就立刻進(jìn)行的
29、操作,幾個(gè)標(biāo)志(通過(guò)<linux/poll.h> 定義)用來(lái)指示可能的操作標(biāo)志含義POLLIN如果設(shè)備無(wú)阻塞的讀,就返回該值POLLRDNORM通常的數(shù)據(jù)已經(jīng)準(zhǔn)備好,可以讀了,就返回該值。通常的做法是會(huì)返回(POLLLIN|POLLRDNORA )POLLRDBAND如果可以從設(shè)備讀出帶外數(shù)據(jù),就返回該值,它只可在 linux內(nèi)核的某些網(wǎng)絡(luò)代碼中使用,通常不用在設(shè)備驅(qū)動(dòng) 程序中POLLPRI如果可以無(wú)阻塞的讀取高優(yōu)先級(jí)(帶外)數(shù)據(jù),就返回該值,返回該值會(huì)導(dǎo)致select報(bào)告文件發(fā)生異常,以為select八帶外數(shù)據(jù)當(dāng)作異常處理POLLHUP當(dāng)讀設(shè)備的進(jìn)程到達(dá)文件尾時(shí),驅(qū)動(dòng)程序必須返回
30、該值,依照select的功能描述,調(diào)用select的進(jìn)程被告知進(jìn)程時(shí)可讀的。POLLERR如果設(shè)備發(fā)生錯(cuò)誤,就返回該值。POLLOUT如果設(shè)備可以無(wú)阻塞地些,就返回該值POLLWRNORM設(shè)備已經(jīng)準(zhǔn)備好,可以寫(xiě)了,就返回該值。通常地做法是(POLLOUT|POLLNORM )POLLWRBAND于 POLLRDBAND 類似考慮poll方法的scullpipe 實(shí)現(xiàn):staticunsigned int scull_p_poll(struct file *filp , poll_table*wait)struct scull_pipe *dev = filp -> private_dat
31、a ;unsigned int mask = 0 ;/* The buffer is circular; it is con sidered full* if "wp" is right behind "rp" and empty if the* two are equal.*/dow n(& dev->sem);poll_wait(filp , &dev->inq, wait );poll_wait(filp , &dev-> outq , wait );if (dev-> rp != dev ->
32、wp)mask|= POLLIN | POLLRDNORM* readable */if (spacefree (dev)mask|= POLLOUT| POLLWRNORIW writable */up (& dev-> sen);return mask;與read 和 write 的交互正確實(shí)現(xiàn)poll調(diào)用的規(guī)則從設(shè)備讀取數(shù)據(jù):(1)如果在輸入緩沖中有數(shù)據(jù),read調(diào)用應(yīng)當(dāng)立刻返回,即便數(shù)據(jù)少于應(yīng)用程序要求的,并確保其他的數(shù)據(jù)會(huì)很快到達(dá)。如果方便,可一直返回小于請(qǐng)求的數(shù)據(jù),但至少返回一個(gè)字節(jié)。在這個(gè)情況下,poll應(yīng)當(dāng)返回POLLIN|POLLRDNORM 。(2)如果在輸入
33、緩沖中無(wú)數(shù)據(jù),read默認(rèn)必須阻塞直到有一個(gè)字節(jié)。若O NONBLOCK 被置位,read立刻返回-EAGIN 。在這個(gè)情況下,poll必須報(bào)告這個(gè)設(shè)備是不可讀(清零POLLIN|POLLRDNORM )的直到至少一個(gè)字節(jié)到達(dá)。(3)若處于文件尾,不管是否阻塞,read應(yīng)當(dāng)立刻返回0,且poll應(yīng)該返回POLLHUP。向設(shè)備寫(xiě)數(shù)據(jù)(1) 若輸出緩沖有空間,write應(yīng)立即返回。它可接受小于調(diào)用所請(qǐng)求的數(shù)據(jù),但至少必須接受一個(gè)字節(jié)。在這個(gè)情況下,poll應(yīng)返回POLLOUT|POLLWRNORM 。(2) 若輸出緩沖是滿的,write默認(rèn)阻塞直到一些空間被釋放。若O_NOBLOCK被設(shè)置,wri
34、te立刻返回一個(gè)-EAGAIN。在這些情況下,poll應(yīng)當(dāng)報(bào)告文件是不可寫(xiě)的(清零POLLOUT|POLLWRNORM ).若設(shè)備不能接受任何多余數(shù)據(jù),不管是否設(shè)置了 O_NON BLOCK , write應(yīng)返回-ENOSPC("設(shè)備上沒(méi)有空間")。(3) 永遠(yuǎn)不要讓write在返回前等待數(shù)據(jù)的傳輸結(jié)束,即使 O_NONBLOCK被清除。若程序想保證它加入到輸出緩沖中的數(shù)據(jù)被真正傳送,驅(qū)動(dòng)必須提供一 個(gè)fsync方法。刷新待處理輸出若一些應(yīng)用程序需要確保數(shù)據(jù)被發(fā)送到設(shè)備, 就實(shí)現(xiàn)必須fsync方法。對(duì)fsync的調(diào)用只在設(shè)備被完全刷新時(shí)(即輸出緩沖為空)才返回,不管O_NO
35、NBLOCK 是否被設(shè)置,即便這需要一些時(shí)間。其原型是:int (* fsync) (struct file *file , struct dentry*dentry , intdatas ync);底層數(shù)據(jù)結(jié)構(gòu)只要用戶應(yīng)用程序調(diào)用 poll、select、或epoll_ctl,內(nèi)核就會(huì)調(diào)用這個(gè)系統(tǒng)調(diào)用所引用的所有文件的poll方法,并向他們傳遞同一個(gè)poll_table 。poll_table結(jié)構(gòu)只是構(gòu)成實(shí)際數(shù)據(jù)結(jié)構(gòu)的簡(jiǎn)單封裝:struct poll_table_struct ;/* structures and helpers for f_op->poll impleme ntati
36、 ons*/typedef void (* poll_queue_proc )( struct file *, wait_queue_head_t *, struct poll_table_struct*);typedef struct poll_table_struct poll_queue_proc qproc; poll_table ;對(duì)于poll和select系統(tǒng)調(diào)用,poll_table 是一個(gè)包含 poll_table_entry 結(jié)構(gòu)內(nèi)存頁(yè)鏈表。struct poll_table_e ntrystruct file * filp ;wait_queue_t wait ;wait_
37、queue_head_t * wait_address ;對(duì)poll_wait 的調(diào)用有時(shí)還會(huì)將進(jìn)程添加到給定的等待隊(duì)列。整個(gè)的結(jié)構(gòu)必須由內(nèi)核維護(hù),在poll或者select返回前,進(jìn)程可從所有的隊(duì)列中去除如果被輪詢的驅(qū)動(dòng)沒(méi)有一個(gè)驅(qū)動(dòng)程序指明可進(jìn)行非阻塞 I/O,poll調(diào)用會(huì)簡(jiǎn)單地睡眠,直到一個(gè)它所在的等待隊(duì)列 (可能許多)喚醒它.當(dāng)poll調(diào)用完成,poll_table 結(jié)構(gòu)被重新分配,所有的之前加入到poll表的等待隊(duì)列入口都會(huì)從表和它們的等待隊(duì)列中移出.struct poll_wqueues poll_table pt ;struct poll_table_page * table ;
38、int error ;int inline_index;struct poll_table_e ntryinline entries N INLINE POLL ENTRIES;struct poll_table_page struct poll_table_page * next ;struct poll_table_entry * entry ;struct poll_table_e ntry en tries 0;;A阿就d胡w琲曲曲 in'rfi itjwal t_q u<?ue_he3d_ tfk丿An袖 anattiYep i'IhestFiicrpoll_t
39、able_struct異步通知通過(guò)使用異步通知,應(yīng)用程序可以在數(shù)據(jù)可用時(shí)收到一個(gè)信號(hào),而無(wú)需不停地輪詢啟用步驟:知道信號(hào)到達(dá)時(shí)該通知哪個(gè)進(jìn)程。(2)使用fcntl系統(tǒng)調(diào)用,通過(guò)F_SETFL命令設(shè)置FASYNC標(biāo)志。內(nèi)核操作過(guò)程1. F_SETOWN 被調(diào)用時(shí) filp->f_owner被賦值。2. 當(dāng)F_SETFL被執(zhí)行來(lái)打開(kāi)FASYNC,驅(qū)動(dòng)的fasync方法被調(diào)用.這個(gè)標(biāo)志在文件被打開(kāi)時(shí)缺省地被清除。3. 當(dāng)數(shù)據(jù)到達(dá)時(shí),所有的注冊(cè)異步通知的進(jìn)程都會(huì)被發(fā)送一個(gè)SIGIO信號(hào)Linux提供的通用方法是基于一個(gè)數(shù)據(jù)結(jié)構(gòu)和兩個(gè)函數(shù),定義在<linux/fs.h>數(shù)據(jù)結(jié)構(gòu):str
40、uct fasync_structintmagic ;intfa_fd;structfasync_struct*fa_next; /* singly linked list */struct file*fa_file ;;驅(qū)動(dòng)調(diào)用的兩個(gè)函數(shù)的原型:int fasync_helper (int fd , struct file *filp , int mode, structfasyn c_struct * fa);void kill_fasync(struct fasync_struct * fa , int sig , int band);當(dāng)一個(gè)打開(kāi)的文件的FASYNC標(biāo)志被修改時(shí),調(diào)用fa
41、sync_helper 來(lái)從相關(guān)的進(jìn)程列表中添加或去除文件。除了最后一個(gè)參數(shù) ,其他所有參數(shù)都時(shí)被提供給fasync方法的相同參數(shù)并被直接傳遞。當(dāng)數(shù)據(jù)到達(dá)時(shí),kill_fasync 被用來(lái)通知相關(guān)的進(jìn)程,它的參數(shù)是被傳遞的信號(hào)(常常是SIGIO)和band (幾乎都是POLL_IN )。這是scullpipe 實(shí)現(xiàn)fasync方法的:static int scull_p_fasync(int fd , struct file *filp , int mode)struct scull_pipe *dev = filp -> private_data ;return fasync_help
42、er (fd , filp , mode, &dev->async_queue);當(dāng)數(shù)據(jù)到達(dá),下面的語(yǔ)句必須被執(zhí)行來(lái)通知異步讀者.因?yàn)閷?duì)sucllpipe讀者的新數(shù)據(jù)通過(guò)一個(gè)發(fā)岀 write的進(jìn)程被產(chǎn)生,這個(gè)語(yǔ)句岀現(xiàn)在scullpipe 的write 方法中:if (dev-> asyn c_queue)kill_fasync(&dev->async_queue, SIGIO, POLL_IN); /* 注意,一些設(shè)備也針對(duì)設(shè)備可寫(xiě)而實(shí)現(xiàn)了異步通知,在這個(gè)情況,kill_fas nyc必須以POLL_OUT模式調(diào)用.*/當(dāng)文件被關(guān)閉時(shí)必須調(diào)用fasync方法,
43、來(lái)從活動(dòng)的異步讀取進(jìn)程列表中刪除該文件。盡管這個(gè)調(diào)用僅當(dāng)filp->f_flags 被設(shè)置為FASYNC時(shí)才需要,但不管什么情況,調(diào)用這個(gè)函數(shù)不會(huì)有問(wèn)題,并且是普遍的實(shí)現(xiàn)方法。以下是scullpipe的release 方法的一部分:/* remove this filp from the asynchronously notified filp's */scull_p_fasync (- 1, filp , 0);異步通知使用的數(shù)據(jù)結(jié)構(gòu)和 struct wait_queue幾乎相同,因?yàn)樗麄兌忌婕暗却录?。區(qū)別異步通知用struct file 替代struct task_stru
44、ct.隊(duì)列中的file用獲取f_owner, 一邊給進(jìn)程發(fā)送信號(hào)。scullpipe 的實(shí)驗(yàn)(poll和fasync 方法的實(shí)現(xiàn))模塊程序鏈接:scullpipe模塊測(cè)試程序鏈接:scullpipe-testARM9實(shí)驗(yàn)板的實(shí)驗(yàn)現(xiàn)象是:Tekkaman244OSBC2440V4d / lib / modules/Tekkaman244OSBC2440Vinsmod pipe . koTekkaman244OSBC2440V4at / proc / devicesCharacter devices :1 mem2 pty3 ttyp4 / dev/ vc/ 04 tty4 ttyS5 / dev
45、/ tty5 / dev/ con sole5 / dev/ ptmx7 vcs10 misc13 in put14 sound81 video4li nux89 i2c90 mtd116 alsa128 ptm136 pts180 usb189 usb_device204 s3c2410_serial252 pipe253 usb_e ndpo int254 rtcBlock devices :1 ramdisk256 rfd7 loop31 mtdblock93 nftl96 inftl179 mmcTekkama n244OSBC2440V4d / dev/Tekkama n244OSB
46、C2440V4Tekkama n244OSBC2440V4d / tmp/Tekkaman244OSBC2440V4 pipe_test &Tekkama n244OSBC2440V4pe n scullpipe0!ope n scullpipe1!SCULL_P_IOCTSIZE scull_p_bufferO=21 !SCULL_PO CTSIZE: scull_p_buffer1=21 !close pipetestO!close pipetest1!reope n scullpipeO!reope n scullpipe1!Tekkama n2440SBC2440V4cho 1
47、2345678901234567890 > /dev/ scullpipeOTekkama n244OSBC2440Vrfead from pipetestO code=200=1 1=2 2=3 3=4 4=55=6 6=7 7=8 8=9 9=010=1 11= 2 12= 3 13= 4 14= 515= 6 16= 7 17= 8 18= 9 19= 0read from pipetestO code =10=1=2 2=3 3=4 4=55=6 6=7 7=8 8=9 9=010=1 11=2 12= 3 13= 4 14= 515= 6 16= 7 17= 8 18= 9 19= 0/ dev/ scullpipe1=15Tekkama n244OSBC2440V4cho 12345678901234 >Tekkama
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 二零二五年度護(hù)校與養(yǎng)老機(jī)構(gòu)合作服務(wù)合同3篇
- 女生節(jié)活動(dòng)策劃方案(3篇)
- 中小學(xué)校實(shí)驗(yàn)室內(nèi)部管理制度范文(二篇)
- 2025年度物流運(yùn)輸安全環(huán)保服務(wù)協(xié)議范本3篇
- 液壓銑床課程設(shè)計(jì)摘要
- 財(cái)務(wù)分析圖表課程設(shè)計(jì)
- 平路機(jī)安全操作規(guī)程范文(2篇)
- 二零二五年度房地產(chǎn)租賃權(quán)包銷合同3篇
- 2025年上半年安全員工作總結(jié)(3篇)
- 2024年滬教版高三歷史上冊(cè)階段測(cè)試試卷
- 2025年競(jìng)聘醫(yī)院內(nèi)科醫(yī)生崗位演講稿模版(3篇)
- 虛擬貨幣地址分析技術(shù)的研究-洞察分析
- 綠色供應(yīng)鏈管理制度內(nèi)容
- 無(wú)錫市區(qū)2024-2025學(xué)年四年級(jí)上學(xué)期數(shù)學(xué)期末試題一(有答案)
- 血液凈化中心院內(nèi)感染控制課件
- 一年級(jí)數(shù)學(xué)(上)計(jì)算題專項(xiàng)練習(xí)集錦
- 年產(chǎn)1.5萬(wàn)噸長(zhǎng)鏈二元酸工程建設(shè)項(xiàng)目可研報(bào)告
- 《北航空氣動(dòng)力學(xué)》課件
- 紡織廠消防管道安裝協(xié)議
- 【MOOC】思辨式英文寫(xiě)作-南開(kāi)大學(xué) 中國(guó)大學(xué)慕課MOOC答案
- 期末測(cè)試卷(試題)-2024-2025學(xué)年五年級(jí)上冊(cè)數(shù)學(xué)北師大版
評(píng)論
0/150
提交評(píng)論