![Linux設(shè)備驅(qū)動(dòng)程序?qū)W習(xí)_第1頁](http://file4.renrendoc.com/view/bf97af2fa29ac2855e1a93f673d26909/bf97af2fa29ac2855e1a93f673d269091.gif)
![Linux設(shè)備驅(qū)動(dòng)程序?qū)W習(xí)_第2頁](http://file4.renrendoc.com/view/bf97af2fa29ac2855e1a93f673d26909/bf97af2fa29ac2855e1a93f673d269092.gif)
![Linux設(shè)備驅(qū)動(dòng)程序?qū)W習(xí)_第3頁](http://file4.renrendoc.com/view/bf97af2fa29ac2855e1a93f673d26909/bf97af2fa29ac2855e1a93f673d269093.gif)
![Linux設(shè)備驅(qū)動(dòng)程序?qū)W習(xí)_第4頁](http://file4.renrendoc.com/view/bf97af2fa29ac2855e1a93f673d26909/bf97af2fa29ac2855e1a93f673d269094.gif)
![Linux設(shè)備驅(qū)動(dòng)程序?qū)W習(xí)_第5頁](http://file4.renrendoc.com/view/bf97af2fa29ac2855e1a93f673d26909/bf97af2fa29ac2855e1a93f673d269095.gif)
版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
Linux設(shè)備驅(qū)動(dòng)程序?qū)W習(xí)(10)時(shí)間、延遲及延緩操作Linux設(shè)備驅(qū)動(dòng)程序?qū)W習(xí)(10)時(shí)間、延遲及延緩操作度量時(shí)間差時(shí)鐘中斷由系統(tǒng)定時(shí)硬件以周期性的間隔產(chǎn)生,這個(gè)間隔由內(nèi)核根據(jù)HZ值來設(shè)定,HZ是一個(gè)體系依賴的值,在<linux/param.h>中定義或該文件包含的某個(gè)子平臺(tái)相關(guān)文件中。作為通用的規(guī)則,即便如果知道HZ的值,在編程時(shí)應(yīng)當(dāng)不依賴這個(gè)特定值,而始終使用HZ。對(duì)于當(dāng)前版本,我們應(yīng)完全信任內(nèi)核開發(fā)者,他們已經(jīng)選擇了最適合的HZ值,最好保持HZ的默認(rèn)值。對(duì)用戶空間,內(nèi)核HZ幾乎完全隱藏,用戶HZ始終擴(kuò)展為100。當(dāng)用戶空間程序包含param.h,且每個(gè)報(bào)告給用戶空間的計(jì)數(shù)器都做了相應(yīng)轉(zhuǎn)換。對(duì)用戶來說確切的HZ值只能通過/proc/interrupts獲得:/proc/interrupts的計(jì)數(shù)值除以/proc/uptime中報(bào)告的系統(tǒng)運(yùn)行時(shí)間。對(duì)于ARM體系結(jié)構(gòu):在<linux/param.h>文件中的定義如下:#ifdef__KERNEL__defineHZCONFIG_HZ*/defineUSER_HZ100/*用戶空間使用的HZ,Userinterfacesarein"ticks"*/defineCLOCKS_PER_SEC(USER_HZ)/*liketimes()*/#elsedefineHZ100#endif也就是說:HZ由__KERNEL__和CONFIG_HZ決定。若未定義__KERNEL__,HZ為100;否則為CONFIG_HZ。而CONFIG_HZ是在內(nèi)核的根目錄的.config文件中定義,并沒有在makemenuconfig的配置選項(xiàng)中出現(xiàn)。Linux的\arch\arm\configs\s3c2410_defconfig文件中的定義為:#KernelFeatures#CONFIG_PREEMPTisnotsetCONFIG_NO_IDLE_HZisnotsetCONFIG_HZ=200CONFIG_AEABIisnotsetCONFIG_ARCH_DISCONTIGMEM_ENABLEisnotset所以正常情況下s3c24x0的HZ為200。這一數(shù)值在后面的實(shí)驗(yàn)中可以證實(shí)。每次發(fā)生一個(gè)時(shí)鐘中斷,內(nèi)核內(nèi)部計(jì)數(shù)器的值就加一。這個(gè)計(jì)數(shù)器在系統(tǒng)啟動(dòng)時(shí)初始化為0,因此它代表本次系統(tǒng)啟動(dòng)以來的時(shí)鐘嘀噠數(shù)。這個(gè)計(jì)數(shù)器是一個(gè)64-位變量(即便在32-位的體系上)并且稱為“jiffies_64,但是驅(qū)動(dòng)通常訪問jiffies變量(unsignedlong)(根據(jù)體系結(jié)構(gòu)的不同:可能是jiffies_64,可能是jiffies_64的低32位)。使用jiffies是首選,因?yàn)樗L問更快,且無需在所有的體系上實(shí)現(xiàn)原子地訪問64-位的jiffies_64值。使用jiffies計(jì)數(shù)器這個(gè)計(jì)數(shù)器和用來讀取它的工具函數(shù)包含在<linux/jiffies.h>,通常只需包含<linux/sched.h>,它會(huì)自動(dòng)放入jiffies.h。jiffies和jiffies_64必須被當(dāng)作只讀變量。當(dāng)需要記錄當(dāng)前jiffies值(被聲明為volatile避免編譯器優(yōu)化內(nèi)存讀)時(shí),可以簡(jiǎn)單地訪問這個(gè)unsignedlong變量,如:#include<linux/jiffies.h>unsignedlongj,stamp_1,stamp_half,stamp_n;j=jiffies;/*readthecurrentvalue*/stamp_1=j+HZ;/*1secondinthefuture*/stamp_half=j+HZ/2;/*halfasecond*/stamp_n=j+n*HZ/1000;/*nmilliseconds*/以下是一些簡(jiǎn)單的工具宏及其定義:#definetime_after(a,b)\(typecheck(unsignedlong,a)&&\typecheck(unsignedlong,b)&&\((long)(b)-(long)(a)<0))#definetime_before(a,b)time_after(b,a)#definetime_after_eq(a,b)\(typecheck(unsignedlong,a)&&\typecheck(unsignedlong,b)&&\((long)(a)-(long)(b)>=0))#definetime_before_eq(a,b)time_after_eq(b,a)用戶空間的時(shí)間表述法(structtimeval和structtimespec)與內(nèi)核表述法的轉(zhuǎn)換函數(shù):#include<linux/time.h>/*#include\kernel\time.c*/structtimespec(time_ttv_sec;longtv_nsec;};#endifstructtimeval(time_ttv_sec;suseconds_ttv_usec;};unsignedlongtimespec_to_jiffies(structtimespec*value);voidjiffies_to_timespec(unsignedlongjiffies,structtimespec*value);unsignedlongtimeval_to_jiffies(structtimeval*value);voidjiffies_to_timeval(unsignedlongjiffies,structtimeval*value);訪問jiffies_64對(duì)于32-位處理器不是原子的,這意味著如果這個(gè)變量在你正在讀取它們時(shí)被更新你可能讀到錯(cuò)誤的值。若需要訪問jiffies_64,內(nèi)核有一個(gè)特別的輔助函數(shù),為你完成適當(dāng)?shù)逆i定:#include<linux/jiffies.h>u64get_jiffies_64(void);處理器特定的寄存器若需測(cè)量非常短時(shí)間間隔或需非常高的精度,可以借助平臺(tái)依賴的資源。許多現(xiàn)代處理器包含一個(gè)隨時(shí)鐘周期不斷遞增的計(jì)數(shù)寄存器,他是進(jìn)行高精度的時(shí)間管理任務(wù)唯一可靠的方法。最有名的計(jì)數(shù)器寄存器是TSC(timestampcounter),在x86的Pentium處理器開始引入并在之后所有的CPU中出現(xiàn)(包括x86_64平臺(tái))。它是一個(gè)64-位寄存器,計(jì)數(shù)CPU的時(shí)鐘周期,可從內(nèi)核和用戶空間讀取。在包含了<asm/msr.h>(一個(gè)x86-特定的頭文件,它的名子代表"machine-specificregisters")的代碼中可使用這些宏:rdtsc(low32,high32);/*原子地讀取64-位TSCrdtscl(low32);/*讀取TSC的低32位到一個(gè)32-位變量*/rdtscll(var64);/*讀64-位TSC值到一個(gè)longlong變量*//*下面的代碼行測(cè)量了指令自身的執(zhí)行時(shí)間:*/unsignedlongini,end;rdtscl(ini);rdtscl(end);printk("timelapse:%li\n",end-ini);一些其他的平臺(tái)提供相似的功能,并且內(nèi)核頭文件提供一個(gè)體系無關(guān)的功能用來代替rdtsc,稱get_cycles(定義在<asm/timex.h>(由<linux/timex.h>包含)),原型如下:#include<linux/timex.h>cycles_tget_cycles(void);/*這個(gè)函數(shù)在每個(gè)平臺(tái)都有定義,但在沒有時(shí)鐘周期計(jì)數(shù)器的平臺(tái)上返回0*//*由于s3c2410系列處理器上沒有時(shí)鐘周期計(jì)數(shù)器所以get_cycles定義如下:*/typedefunsignedlongcycles_t;staticinlinecycles_tget_cycles(void)(return0;}獲取當(dāng)前時(shí)間驅(qū)動(dòng)一般無需知道時(shí)鐘時(shí)間(用年月日、小時(shí)、分鐘、秒來表達(dá)的時(shí)間),只對(duì)用戶程序才需要,如cron和syslogd。內(nèi)核提供了一個(gè)將時(shí)鐘時(shí)間轉(zhuǎn)變?yōu)槊霐?shù)值的函數(shù):unsignedlongmktime(constunsignedintyear0,constunsignedintmon0constunsignedintday,constunsignedinthourconstunsignedintmin,constunsignedintsec)(unsignedintmon=mon0,year=year0;/*1..12->11,12,1..10*/if(0>=(int)(mon-=2))(mon+=12;/*PutsFeblastsinceithasleapday*/year-=1;}return((((unsignedlong)(year/4-year/100+year/400+367*mon/12+day)+year*365-719499)*24+hour/*nowhavehours*/)*60+min/*nowhaveminutes*/)*60+sec;/*finallyseconds*/}/*這個(gè)函數(shù)將時(shí)間轉(zhuǎn)換成從1970年1月1日0小時(shí)0分0秒到你輸入的時(shí)間所經(jīng)過的秒數(shù),溢出時(shí)間為2106-02-0706:28:16。本人認(rèn)為這個(gè)函數(shù)的使用應(yīng)這樣:若你要計(jì)算2000-02-0706:28:16到2000-02-0906:28:16所經(jīng)過的秒數(shù):unsignedlongtime1=mktime(2000,2,7,6,28,16)-mktime(2000,2,9,6,28,16);若還要轉(zhuǎn)成jiffies,就再加上:unsignedlongtime2=time1*HZ.注意溢出的情況!*/為了處理絕對(duì)時(shí)間,<linux/time.h>導(dǎo)出了do_gettimeofday函數(shù),它填充一個(gè)指向structtimeval的指針變量。絕對(duì)時(shí)間也可來自xtime變量,一個(gè)structtimespec值,為了原子地訪問它,內(nèi)核提供了函數(shù)current_kernel_time。它們的精確度由硬件決定,原型是:#include<linux/time.h>voiddo_gettimeofday(structtimeval*tv);structtimespeccurrent_kernel_time(void);/*得到的數(shù)據(jù)都表示當(dāng)前時(shí)間距UNIX時(shí)間基準(zhǔn)1970-01-0100:00:00的相對(duì)時(shí)間*/以上兩個(gè)函數(shù)在ARM平臺(tái)都是通過xtime變量得到數(shù)據(jù)的。全局變量xtime:它是一個(gè)timeval結(jié)構(gòu)類型的變量,用來表示當(dāng)前時(shí)間距UNIX時(shí)間基準(zhǔn)1970-01-0100:00:00的相對(duì)秒數(shù)值。結(jié)構(gòu)timeval是Linux內(nèi)核表示時(shí)間的一種格式(Linux內(nèi)核對(duì)時(shí)間的表示有多種格式,每種格式都有不同的時(shí)間精度),其時(shí)間精度是微秒。該結(jié)構(gòu)是內(nèi)核表示時(shí)間時(shí)最常用的一種格式,它定義在頭文件include/linux/time.h中,如下所示:structtimeval{time_ttv_sec;/*seconds*/suseconds_ttv_usec;/*microseconds*/};其中,成員tv_sec表示當(dāng)前時(shí)間距UNIX時(shí)間基準(zhǔn)的秒數(shù)值,而成員tv_usec則表示一秒之內(nèi)的微秒值,且1O00O0O>tv_usec>=0。Linux內(nèi)核通過timeval結(jié)構(gòu)類型的全局變量xtime來維持當(dāng)前時(shí)間,該變量定義在kernel/timer.c文件中,如下所示:/*Thecurrenttime*/volatilestructtimevalxtime_attribute_((aligned(16)));但是,全局變量xtime所維持的當(dāng)前時(shí)間通常是供用戶來檢索和設(shè)置的,而其他內(nèi)核模塊通常很少使用它(其他內(nèi)核模塊用得最多的是jiffies),因此對(duì)xtime的更新并不是一項(xiàng)緊迫的任務(wù),所以這一工作通常被延遲到時(shí)鐘中斷的底半部(bottomhalf)中來進(jìn)行。由于bottomhalf的執(zhí)行時(shí)間帶有不確定性,因此為了記住內(nèi)核上一次更新xtime是什么時(shí)候,Linux內(nèi)核定義了一個(gè)類似于jiffies的全局變量wall_jiffies,來保存內(nèi)核上一次更新xtime時(shí)的jiffies值。時(shí)鐘中斷的底半部分每一次更新xtime的時(shí)侯都會(huì)將wall_jiffies更新為當(dāng)時(shí)的jiffies值。全局變量wall_jiffies定義在kernel/timer.c文件中:/*jiffiesatthemostrecentupdateofwalltime*/unsignedlongwall_jiffies;原文網(wǎng)址:/freedom1013/archive/2007/03/13/1528310.aspx延遲執(zhí)行設(shè)備驅(qū)動(dòng)常常需要延后一段時(shí)間執(zhí)行一個(gè)特定片段的代碼,常常允許硬件完成某個(gè)任務(wù).長(zhǎng)延遲有時(shí),驅(qū)動(dòng)需要延后執(zhí)行相對(duì)長(zhǎng)時(shí)間,長(zhǎng)于一個(gè)時(shí)鐘嘀噠。忙等待(盡量別用)若想延遲執(zhí)行若干個(gè)時(shí)鐘嘀噠,精度要求不高。最容易的(盡管不推薦)實(shí)現(xiàn)是一個(gè)監(jiān)視jiffy計(jì)數(shù)器的循環(huán)。這種忙等待實(shí)現(xiàn)的代碼如下:while(time_before(jiffies,j1))cpu_relax();對(duì)cpu_relex的調(diào)用將以體系相關(guān)的方式執(zhí)行,在許多系統(tǒng)中它根本不做任何事,這個(gè)方法應(yīng)當(dāng)明確地避免。對(duì)于ARM體系來說:#definecpu_relax()barrier()也就是說在ARM上運(yùn)行忙等待相當(dāng)于:while(time_before(jiffies,j1));這種忙等待嚴(yán)重地降低了系統(tǒng)性能。如果未配置內(nèi)核為搶占式,這個(gè)循環(huán)在延時(shí)期間完全鎖住了處理器,計(jì)算機(jī)直到時(shí)間ji到時(shí)會(huì)完全死掉。如果運(yùn)行一個(gè)可搶占的內(nèi)核時(shí)會(huì)改善一點(diǎn),但是忙等待在可搶占系統(tǒng)中仍然是浪費(fèi)資源的。更糟的是,當(dāng)進(jìn)入循環(huán)時(shí)如果中斷碰巧被禁止,jiffies將不會(huì)被更新,并且while條件永遠(yuǎn)保持真,運(yùn)行一個(gè)搶占的內(nèi)核也不會(huì)有幫助,唯一的解決方法是重啟。讓出處理器忙等待加重了系統(tǒng)負(fù)載,必須找出一個(gè)更好的技術(shù):不需要CPU時(shí)釋放CPU。這可通過調(diào)用schedule函數(shù)實(shí)現(xiàn)(在<linux/sched.h>中聲明):while(time_before(jiffies,j1))(schedule();}在計(jì)算機(jī)空閑時(shí)運(yùn)行空閑任務(wù)(進(jìn)程號(hào)0,由于歷史原因也稱為swapper)可減輕處理器工作負(fù)載、降低溫度、增加壽命。超時(shí)實(shí)現(xiàn)延遲的最好方法應(yīng)該是讓內(nèi)核為我們完成相應(yīng)的工作。若驅(qū)動(dòng)使用一個(gè)等待隊(duì)列來等待某些其他事件,并想確保它在一個(gè)特定時(shí)間段內(nèi)運(yùn)行,可使用:#include<linux/wait.h>longwait_event_timeout(wait_queue_head_tq,condition,longtimeout);longwait_event_interruptible_timeout(wait_queue_head_tq,condition,longtimeout);/*這些函數(shù)在給定隊(duì)列上睡眠,但是它們?cè)诔瑫r(shí)(以jiffies表示)到后返回。如果超時(shí),函數(shù)返回0;如果這個(gè)進(jìn)程被其他事件喚醒,則返回以jiffies表示的剩余的延遲實(shí)現(xiàn);返回值從不會(huì)是負(fù)值*/為了實(shí)現(xiàn)進(jìn)程在超時(shí)到期時(shí)被喚醒而又不等待特定事件(避免聲明和使用一個(gè)多余的等待隊(duì)列頭),內(nèi)核提供了schedule_timeout函數(shù):#include<linux/sched.h>signedlongschedule_timeout(signedlongtimeout);/*timeout是要延時(shí)的jiffies數(shù)。除非這個(gè)函數(shù)在給定的timeout流失前返回,否則返回值是0。schedule_timeout要求調(diào)用者首先設(shè)置當(dāng)前的進(jìn)程狀態(tài)。為獲得一個(gè)不可中斷的延遲,可使用TASK_UNINTERRUPTIBLE代替。如果你忘記改變當(dāng)前進(jìn)程的狀態(tài),調(diào)用schedule_time如同調(diào)用shcedule,建立一個(gè)不用的定時(shí)器。一個(gè)典型調(diào)用如下:*/set_current_state(TASK_INTERRUPTIBLE);schedule_timeout(delay);短延遲當(dāng)一個(gè)設(shè)備驅(qū)動(dòng)需要處理硬件的延遲(latency潛伏期),涉及到的延時(shí)通常最多幾個(gè)毫秒,在這個(gè)情況下,不應(yīng)依靠時(shí)鐘嘀噠,而是內(nèi)核函數(shù)ndelay,udelay和mdelay,他們分別延后執(zhí)行指定的納秒數(shù),微秒數(shù)或者毫秒數(shù),定義在<asm/delay.h>,原型如下:#include<linux/delay.h>voidndelay(unsignedlongnsecs);voidudelay(unsignedlongusecs);voidmdelay(unsignedlongmsecs);重要的是記住這3個(gè)延時(shí)函數(shù)是忙等待;其他任務(wù)在時(shí)間流失時(shí)不能運(yùn)行。每個(gè)體系都實(shí)現(xiàn)udelay,但是其他的函數(shù)可能未定義;如果它們沒有定義,<linux/delay.h>提供一個(gè)缺省的基于udelay的版本。在所有的情況中,獲得的延時(shí)至少是要求的值,但可能更多。udelay的實(shí)現(xiàn)使用一個(gè)軟件循環(huán),它基于在啟動(dòng)時(shí)計(jì)算的處理器速度和使用整數(shù)變量loos_per_jiffy確定循環(huán)次數(shù)。為避免在循環(huán)計(jì)算中整數(shù)溢出,傳遞給udelay和ndelay的值有一個(gè)上限,如果你的模塊無法加載和顯示一個(gè)未解決的符號(hào):_bad_udelay,這意味著你調(diào)用udleay時(shí)使用太大的參數(shù)。作為一個(gè)通用的規(guī)則:若試圖延時(shí)幾千納秒,應(yīng)使用udelay而不是ndelay;類似地,毫秒規(guī)模的延時(shí)應(yīng)當(dāng)使用mdelay完成而不是一個(gè)更細(xì)粒度的函數(shù)。有另一個(gè)方法獲得毫秒(和更長(zhǎng))延時(shí)而不用涉及到忙等待的方法是使用以下函數(shù)(在<linux/delay.h>中聲明):voidmsleep(unsignedintmillisecs);unsignedlongmsleep_interruptible(unsignedintmillisecs);voidssleep(unsignedintseconds)若能夠容忍比請(qǐng)求的更長(zhǎng)的延時(shí),應(yīng)使用schedule_timeout,msleep或ssleep。內(nèi)核定時(shí)器當(dāng)需要調(diào)度一個(gè)以后發(fā)生的動(dòng)作,而在到達(dá)該時(shí)間點(diǎn)時(shí)不阻塞當(dāng)前進(jìn)程,則可使用內(nèi)核定時(shí)器。內(nèi)核定時(shí)器用來調(diào)度一個(gè)函數(shù)在將來一個(gè)特定的時(shí)間(基于時(shí)鐘嘀噠)執(zhí)行,從而可完成各類任務(wù)。內(nèi)核定時(shí)器是一個(gè)數(shù)據(jù)結(jié)構(gòu),它告訴內(nèi)核在一個(gè)用戶定義的時(shí)間點(diǎn)使用用戶定義的參數(shù)執(zhí)行一個(gè)用戶定義的函數(shù),函數(shù)位于<linux/timer.h>和kernel/timer.c。被調(diào)度運(yùn)行的函數(shù)幾乎確定不會(huì)在注冊(cè)它們的進(jìn)程在運(yùn)行時(shí)運(yùn)行,而是異步運(yùn)行。實(shí)際上,內(nèi)核定時(shí)器通常被作為一個(gè)"軟件中斷”的結(jié)果而實(shí)現(xiàn)。當(dāng)在進(jìn)程上下文之外(即在中斷上下文)中運(yùn)行程序時(shí),必須遵守下列規(guī)則:不允許訪問用戶空間;current指針在原子態(tài)沒有意義;不能進(jìn)行睡眠或者調(diào)度.例如:調(diào)用kmalloc(...,GFP_KERNEL)是非法的,信號(hào)量也不能使用因?yàn)樗鼈兛赡芩摺Mㄟ^調(diào)用函數(shù)in_interrupt()能夠告知是否它在中斷上下文中運(yùn)行,無需參數(shù)并如果處理器當(dāng)前在中斷上下文運(yùn)行就返回非零。通過調(diào)用函數(shù)in_atomic()能夠告知調(diào)度是否被禁止,若調(diào)度被禁止返回非零;調(diào)度被禁止包含硬件和軟件中斷上下文以及任何持有自旋鎖的時(shí)候。在后一種情況,current可能是有效的,但是訪問用戶空間是被禁止的,因?yàn)樗軐?dǎo)致調(diào)度發(fā)生.當(dāng)使用in_interrupt()時(shí),都應(yīng)考慮是否真正該使用的是in_atomic。他們都在<asm/hardirq.h>中聲明。內(nèi)核定時(shí)器的另一個(gè)重要特性是任務(wù)可以注冊(cè)它本身在后面時(shí)間重新運(yùn)行,因?yàn)槊總€(gè)timer_list結(jié)構(gòu)都會(huì)在運(yùn)行前從激活的定時(shí)器鏈表中去連接,因此能夠立即鏈入其他的鏈表。一個(gè)重新注冊(cè)它自己的定時(shí)器一直運(yùn)行在同一個(gè)CPU.即便在一個(gè)單處理器系統(tǒng),定時(shí)器是一個(gè)潛在的態(tài)源,這是異步運(yùn)行直接結(jié)果。因此任何被定時(shí)器函數(shù)訪問的數(shù)據(jù)結(jié)構(gòu)應(yīng)當(dāng)通過原子類型或自旋鎖被保護(hù),避免并發(fā)訪問定時(shí)器API內(nèi)核提供給驅(qū)動(dòng)許多函數(shù)來聲明、注冊(cè)以及刪除內(nèi)核定時(shí)器:#include<linux/timer.h>structtimer_list(structlist_headentry;unsignedlongexpires;/*期望定時(shí)器運(yùn)行的絕對(duì)jiffies值,不是一個(gè)jiffies_64值,因?yàn)槎〞r(shí)器不被期望在將來很久到時(shí)*/void(*function)(unsignedlong);/*期望調(diào)用的函數(shù)*/unsignedlongdata;/*傳遞給函數(shù)的參數(shù),若需要在參數(shù)中傳遞多個(gè)數(shù)據(jù)項(xiàng),可以將它們捆綁成單個(gè)數(shù)據(jù)結(jié)構(gòu)并且將它的指針強(qiáng)制轉(zhuǎn)換為unsigedlong的指針傳入。這種做法在所有支持的體系上都是安全的并且在內(nèi)存管理中相當(dāng)普遍*/structtvec_t_base_s*base;#ifdefCONFIG_TIMER_STATSvoid*start_site;charstart_comm[16];intstart_pid;#endif};/*這個(gè)結(jié)構(gòu)必須在使用前初始化,以保證所有的成員被正確建立(包括那些對(duì)調(diào)用者不透明的初始化):*/voidinit_timer(structtimer_list*timer);structtimer_listTIMER_INITIALIZER(_function,_expires,_data);/*在初始化后和調(diào)用add_timer前,可以改變3個(gè)公共成員:expires、function和data*/voidadd_timer(structtimer_list*timer);intdel_timer(structtimer_list*timer);/*在到時(shí)前禁止-的定時(shí)器*/intdel_timer_sync(structtimer_list*timer);但還保證當(dāng)它返回時(shí),定時(shí)器函數(shù)不在任何CPU上運(yùn)行,以避免在SMP系統(tǒng)上競(jìng)態(tài),并且在單處理器內(nèi)核中和del_timer相同。這個(gè)函數(shù)應(yīng)當(dāng)在大部分情況下優(yōu)先考慮。如果它被從非原子上下文調(diào)用,這個(gè)函數(shù)可能睡眠,但是在其他情況下會(huì)忙等待。當(dāng)持有鎖時(shí)要小心調(diào)用del_timer_sync,如果這個(gè)定時(shí)器函數(shù)試圖獲得同一個(gè)鎖,系統(tǒng)會(huì)死鎖。如果定時(shí)器函數(shù)重新注冊(cè)自己,調(diào)用者必須首先確保這個(gè)重新注冊(cè)不會(huì)發(fā)生;這通常通過設(shè)置一個(gè)〃關(guān)閉〃標(biāo)志來實(shí)現(xiàn),這個(gè)標(biāo)志被定時(shí)器函數(shù)檢查*/intmod_timer(structtimer_list*timer,unsignedlongexpires);更新一個(gè)定時(shí)器的超時(shí)時(shí)間,常用于超時(shí)定時(shí)器。也可在正常使用add_timer時(shí)在不活動(dòng)的定時(shí)器上調(diào)用mod_timer*/inttimer_pending(conststructtimer_list*timer);timer_list結(jié)構(gòu)中一個(gè)不可見的成員,返回定時(shí)器是否在被調(diào)度運(yùn)行*/內(nèi)核定時(shí)器的實(shí)現(xiàn)《LDD3》介紹的比較籠統(tǒng),以后看《ULK3》的時(shí)候再細(xì)細(xì)研究。一個(gè)內(nèi)核定時(shí)器還遠(yuǎn)未完善,因?yàn)樗艿絡(luò)itter、硬件中斷,還有其他定時(shí)器和其他異步任務(wù)的影響。雖然一個(gè)簡(jiǎn)單數(shù)字I/O關(guān)聯(lián)的定時(shí)器對(duì)簡(jiǎn)單任務(wù)是足夠的,但不合適在工業(yè)環(huán)境中的生產(chǎn)系統(tǒng),對(duì)于這樣的任務(wù),你將最可能需要實(shí)時(shí)內(nèi)核擴(kuò)展(RT-Linux).Tasklets另一個(gè)有關(guān)于定時(shí)的內(nèi)核設(shè)施是tasklet。它類似內(nèi)核定時(shí)器:在中斷時(shí)間運(yùn)行且運(yùn)行同一個(gè)CPU上,并接收一個(gè)unsignedlong參數(shù)。不同的是:無法要求在一個(gè)指定的時(shí)間執(zhí)行函數(shù),只能簡(jiǎn)單地要求它在以后的一個(gè)由內(nèi)核選擇的時(shí)間執(zhí)行。它對(duì)于中斷處理特別有用:硬件中斷必須盡快處理,但大部分的數(shù)據(jù)管理可以延后到以后安全的時(shí)間執(zhí)行。實(shí)際上,一個(gè)tasket,就象一個(gè)內(nèi)核定時(shí)器,在一個(gè)"軟中斷”的上下文中執(zhí)行(以原子模式)。軟件中斷是在使能硬件中斷時(shí)執(zhí)行異步任務(wù)的一個(gè)內(nèi)核機(jī)制。tasklet以一個(gè)數(shù)據(jù)結(jié)構(gòu)形式存在,使用前必須被初始化。初始化能夠通過調(diào)用一個(gè)特定函數(shù)或者通過使用某些宏定義聲明結(jié)構(gòu):#include<linux/interrupt.h>structtasklet_struct(structtasklet_struct*next;unsignedlongstate;atomic_tcount;void(*func)(unsignedlong);unsignedlongdata;};voidtasklet_init(structtasklet_struct*t,void(*func)(unsignedlong),unsignedlongdata);#defineDECLARE_TASKLET(name,func,data)\structtasklet_structname=(NULL,0,ATOMIC_INIT(0),func,data}#defineDECLARE_TASKLET_DISABLED(name,func,data)\structtasklet_structname=(NULL,0,ATOMIC_INIT(1),func,data}voidtasklet_disable(structtasklet_struct*t);/*函數(shù)暫時(shí)禁止給定的tasklet被tasklet_schedule調(diào)度,直到這個(gè)tasklet被再次被enable;若這個(gè)tasklet當(dāng)前在運(yùn)行,這個(gè)函數(shù)忙等待直到這個(gè)tasklet退出*/voidtasklet_disable_nosync(structtasklet_struct*t);/*和tasklet_disable類似,但是tasklet可能仍然運(yùn)行在另一個(gè)CPU*/voidtasklet_enable(structtasklet_struct*t);/*使能一個(gè)之前被disable的tasklet;若這個(gè)tasklet已經(jīng)被調(diào)度,它會(huì)很快運(yùn)行。tasklet_enable和tasklet_disable必須匹配調(diào)用,因?yàn)閮?nèi)核跟蹤每個(gè)tasklet的〃禁止次數(shù)〃*/voidtasklet_schedule(structtasklet_struct*t);/*調(diào)度tasklet執(zhí)行,如果tasklet在運(yùn)行中被調(diào)度,它在完成后會(huì)再次運(yùn)行;這保證了在其他事件被處理當(dāng)中發(fā)生的事件受到應(yīng)有的注意.這個(gè)做法也允許一個(gè)tasklet重新調(diào)度它自己*/voidtasklet_hi_schedule(structtasklet_struct*t);/*和tasklet_schedule類似,只是在更高優(yōu)先級(jí)執(zhí)行。當(dāng)軟中斷處理運(yùn)行時(shí),它處理高優(yōu)先級(jí)tasklet在其他軟中斷之前,只有具有低響應(yīng)周期要求的驅(qū)動(dòng)才應(yīng)使用這個(gè)函數(shù),可避免其他軟件中斷處理引入的附加周期*/voidtasklet_kill(structtasklet_struct*t);/*確保了tasklet不會(huì)被再次調(diào)度來運(yùn)行,通常當(dāng)一個(gè)設(shè)備正被關(guān)閉或者模塊卸載時(shí)被調(diào)用。如果tasklet正在運(yùn)行,這個(gè)函數(shù)等待直到它執(zhí)行完畢。若tasklet重新調(diào)度它自己,則必須阻止在調(diào)用tasklet_kill前它重新調(diào)度它自己,如同使用del_timer_sync*/tasklet的特點(diǎn):一個(gè)tasklet能夠被禁止并且之后被重新使能;它不會(huì)執(zhí)行,直到它被使能與被禁止相同的的次數(shù);如同定時(shí)器,一個(gè)tasklet可以注冊(cè)它自己;一個(gè)tasklet能被調(diào)度來執(zhí)行以正常的優(yōu)先級(jí)或者高優(yōu)先級(jí);如果系統(tǒng)不在重負(fù)載下,taslet可能立刻運(yùn)行,但是從不會(huì)晚于下一個(gè)時(shí)鐘嘀噠;一個(gè)tasklet可能和其他tasklet并發(fā),但是它自己是嚴(yán)格地串行的,且tasklet從不同時(shí)運(yùn)行在不同處理器上,通常在調(diào)度它的同一個(gè)CPU上運(yùn)行。工作隊(duì)列工作隊(duì)列類似taskets,允許內(nèi)核代碼請(qǐng)求在將來某個(gè)時(shí)間調(diào)用一個(gè)函數(shù),不同在于:tasklet在軟件中斷上下文中運(yùn)行,所以tasklet代碼必須是原子的。而工作隊(duì)列函數(shù)在一個(gè)特殊內(nèi)核進(jìn)程上下文運(yùn)行,有更多的靈活性,且能夠休眠。tasklet只能在最初被提交的處理器上運(yùn)行,這只是工作隊(duì)列默認(rèn)工作方式。內(nèi)核代碼可以請(qǐng)求工作隊(duì)列函數(shù)被延后一個(gè)給定的時(shí)間間隔。tasklet執(zhí)行的很快,短時(shí)期,并且在原子態(tài),而工作隊(duì)列函數(shù)可能是長(zhǎng)周期且不需要是原子的,兩個(gè)機(jī)制有它適合的情形。工作隊(duì)列有structworkqueue_struct類型,在<linux/workqueue.h>中定義。一個(gè)工作隊(duì)列必須明確的在使用前創(chuàng)建,宏為:structworkqueue_struct*create_workqueue(constchar*name);structworkqueue_struct*create_singlethread_workqueue(constchar*name);每個(gè)工作隊(duì)列有一個(gè)或多個(gè)專用的進(jìn)程("內(nèi)核線程”),這些進(jìn)程運(yùn)行提交給這個(gè)隊(duì)列的函數(shù)。若使用create項(xiàng)。rkqueue,就得到一個(gè)工作隊(duì)列它在系統(tǒng)的每個(gè)處理器上有一個(gè)專用的線程。在很多情況下,過多線程對(duì)系統(tǒng)性能有影響,如果單個(gè)線程就足夠則使用create_singlethread_workqueue來創(chuàng)建工作隊(duì)列。提交一個(gè)任務(wù)給一個(gè)工作隊(duì)列,在這里《LDD3》介紹的內(nèi)核2.6.10和我用的新內(nèi)核已經(jīng)有不同了,老接口已經(jīng)不能用了,編譯會(huì)出錯(cuò)。這里我只講的新接口,至于老的接口我想今后內(nèi)核不會(huì)再有了。從這一點(diǎn)我們可以看出內(nèi)核發(fā)展。/*需要填充work_struct或delayed_work結(jié)構(gòu),可以在編譯時(shí)完成,宏如下:夫/structwork_struct(atomic_long_tdata;#defineWORK_STRUCT_PENDING0/*Tifworkitempendingexecution*/#defineWORK_STRUCT_FLAG_MASK(3UL)#defineWORK_STRUCT_WQ_DATA_MASK(~WORK_STRUCT_FLAG_MASK)structlist_headentry;work_func_tfunc;};structdelayed_work(structwork_structwork;structtimer_listtimer;};DECLARE_WORK(n,f)/*n是聲明的work_struct結(jié)構(gòu)名稱,f是要從工作隊(duì)列被調(diào)用的函數(shù)*/DECLARE_DELAYED_WORK(n,f)/*n是聲明的delayed_work結(jié)構(gòu)名稱,f是要從工作隊(duì)列被調(diào)用的函數(shù)*//*若在運(yùn)行時(shí)需要建立work_struct或delayed_work結(jié)構(gòu),使用下面2個(gè)宏定義:*/INIT_WORK(structwork_struct*work,void(*function)(void*));PREPARE_WORK(structwork_struct*work,void(*function)(void*));INIT_DELAYED_WORK(structdelayed_work*work,void(*function)(void*));PREPARE_DELAYED_WORK(structdelayed_work*work,void(*function)(void*));/*INIT_*做更加全面的初始化結(jié)構(gòu)的工作,在第一次建立結(jié)構(gòu)時(shí)使用.PREPARE_*做幾乎同樣的工作,但是它不初始化用來連接work_struct或delayed_work結(jié)構(gòu)到工作隊(duì)列的指針。如果這個(gè)結(jié)構(gòu)已經(jīng)被提交給一個(gè)工作隊(duì)列,且只需要修改該結(jié)構(gòu),則使用PREPARE_*而不是INIT_**//*有2個(gè)函數(shù)來提交工作給一個(gè)工作隊(duì)列:*/intqueue_work(structworkqueue_struct*queue,structwork_struct*work);intqueue_delayed_work(structworkqueue_struct*queue,structdelayed_work*work,unsignedlongdelay);/*每個(gè)都添加work到給定的workqueueo如果使用queue_delay_work,則實(shí)際的工作至少要經(jīng)過指定的jiffies才會(huì)被執(zhí)行。這些函數(shù)若返回1則工作被成功加入到隊(duì)列;若為0,則意味著這個(gè)work已經(jīng)在隊(duì)列中等待,不能再次加入*/在將來的某個(gè)時(shí)間,這個(gè)工作函數(shù)將被傳入給定的data值來調(diào)用。這個(gè)函數(shù)將在工作線程的上下文運(yùn)行,因此它可以睡眠(你應(yīng)當(dāng)知道這個(gè)睡眠可能影響提交給同一個(gè)工作隊(duì)列的其他任務(wù))工作函數(shù)不能訪問用戶空間,因?yàn)樗谝粋€(gè)內(nèi)核線程中運(yùn)行,完全沒有對(duì)應(yīng)的用戶空間來訪問。取消一個(gè)掛起的工作隊(duì)列入口項(xiàng)可以調(diào)用:intcancel_delayed_work(structdelayed_work*work);voidcancel_work_sync(structwork_struct*work)如果這個(gè)入口在它開始執(zhí)行前被取消,則返回非零。內(nèi)核保證給定入口的執(zhí)行不會(huì)在調(diào)用cancel_delay_work后被初始化.如果cancel_delay_work返回0,但是,這個(gè)入口可能已經(jīng)運(yùn)行在一個(gè)不同的處理器,并且可能仍然在調(diào)用cancel_delayed_work后在運(yùn)行.要絕對(duì)確保工作函數(shù)沒有在cancel_delayed_work返回0后在任何地方運(yùn)行,你必須跟隨這個(gè)調(diào)用來調(diào)用:voidflush_workqueue(structworkqueue_struct*queue);在flush_workqueue返回后,沒有在這個(gè)調(diào)用前提交的函數(shù)在系統(tǒng)中任何地方運(yùn)行。而cancel_work_sync會(huì)取消相應(yīng)的work,但是如果這個(gè)work已經(jīng)在運(yùn)行那么cancel_work_sync會(huì)阻塞,直到work完成并取消相應(yīng)的work。當(dāng)用完一個(gè)工作隊(duì)列,可以去掉它,使用:voiddestroy_workqueue(structworkqueue_struct*queue);共享隊(duì)列在許多情況下,設(shè)備驅(qū)動(dòng)不需要它自己的工作隊(duì)列。如果你只偶爾提交任務(wù)給隊(duì)列,簡(jiǎn)單地使用內(nèi)核提供的共享的默認(rèn)的隊(duì)列可能更有效。若使用共享隊(duì)列,就必須明白將和其他人共享它,這意味著不應(yīng)當(dāng)長(zhǎng)時(shí)間獨(dú)占隊(duì)列(不能長(zhǎng)時(shí)間睡眠),并且可能要更長(zhǎng)時(shí)間才能獲得處理器。使用的順序:(1)建立work_struct或delayed_workstaticstructwork_structjiq_work;staticstructdelayed_workjiq_work_delay;/*thislineisinjiq_init()*/INIT_WORK(&jiq_work,jiq_print_wq);
INIT_DELAYED_WORK(&jiq_work_delay,jiq_print_wq);(2)提交工作intschedule_work(&jiq_work);/*對(duì)于work_struct結(jié)構(gòu)*/intschedule_delayed_work(&jiq_work_delay,delay);/*對(duì)于delayed_work結(jié)構(gòu)*//*返回值的定義和queue_work一樣*/若需取消一個(gè)已提交給工作隊(duì)列入口項(xiàng),可以使用cancel_delayed_work和cancel_work_sync,但刷新共享隊(duì)列需要一個(gè)特殊的函數(shù):voidflush_scheduled_work(void);因?yàn)椴恢勒l可能使用這個(gè)隊(duì)列,因此不可能知道flush_schduled_work返回需要多長(zhǎng)時(shí)間。ARM9s3c2440AL實(shí)驗(yàn)jiq模塊:jiq實(shí)驗(yàn)數(shù)據(jù):[Tekkaman2440@SBC2440V4]#cd/lib/modules/[Tekkaman2440@SBC2440V4]#insmodjit.ko[Tekkaman2440@SBC2440V4]#head-6/proc/currentime0x0002b82f0x000000010002b82f1191.1190511191.1150000000x0002b82f0x000000010002b82f1191.1192041191.1150000000x0002b82f0x000000010002b82f1191.119230jiq模塊:jiq1191.115000000[Tekkaman2440@SBC2440V4]#ddbs=20count=5/proc/jitbusy2016042018042018042020042020042022042022042024042024042026045+0recordsin5+0recordsout[Tekkaman2440@SBC2440V4]#ddbs=20count=5/proc/jitsched2126402128402128402130402130402132402132402134402134402136405+0recordsin5+0recordsout[Tekkaman2440@SBC2440V4]#ddbs=20count=5/proc/jitqueue2182992184992184992186992186992188992188992190992190992192995+0re
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 工程師年工作計(jì)劃報(bào)告
- 初中化學(xué)教學(xué)計(jì)劃
- 路面硬化施工合同范本
- 托管運(yùn)營(yíng)合作運(yùn)營(yíng)協(xié)議書范本
- 搭建活動(dòng)板房合同范本
- 電子產(chǎn)品經(jīng)銷合同范本
- 上海市超市大型綜合超市蔬菜流通安全協(xié)議書范本
- 保密與競(jìng)業(yè)限制協(xié)議書范本(包括在職期間)
- 五年級(jí)上冊(cè)數(shù)學(xué)聽評(píng)課記錄《3.3 3 的倍數(shù)特征》北師大版
- 深圳信息職業(yè)技術(shù)學(xué)院《工程實(shí)訓(xùn)》2023-2024學(xué)年第二學(xué)期期末試卷
- 2025年買賣個(gè)人房屋合同(4篇)
- 2025代運(yùn)營(yíng)合同范本
- 武漢2025年湖北武漢理工大學(xué)管理人員招聘筆試歷年參考題庫附帶答案詳解
- 第十一章《功和機(jī)械能》達(dá)標(biāo)測(cè)試卷(含答案)2024-2025學(xué)年度人教版物理八年級(jí)下冊(cè)
- 2025年銷售部年度工作計(jì)劃
- 2024年蘇州工業(yè)園區(qū)服務(wù)外包職業(yè)學(xué)院高職單招職業(yè)適應(yīng)性測(cè)試歷年參考題庫含答案解析
- 辦公用品價(jià)格清單
- ESG表現(xiàn)對(duì)企業(yè)財(cái)務(wù)績(jī)效的影響研究
- DB3713T 340-2024 實(shí)景三維數(shù)據(jù)接口及服務(wù)發(fā)布技術(shù)規(guī)范
- 八年級(jí)生物開學(xué)摸底考(長(zhǎng)沙專用)(考試版)
評(píng)論
0/150
提交評(píng)論