版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
Linux設備驅動程序學習(10)時間、延遲及延緩操作Linux設備驅動程序學習(10)時間、延遲及延緩操作度量時間差時鐘中斷由系統(tǒng)定時硬件以周期性的間隔產生,這個間隔由內核根據HZ值來設定,HZ是一個體系依賴的值,在<linux/param.h>中定義或該文件包含的某個子平臺相關文件中。作為通用的規(guī)則,即便如果知道HZ的值,在編程時應當不依賴這個特定值,而始終使用HZ。對于當前版本,我們應完全信任內核開發(fā)者,他們已經選擇了最適合的HZ值,最好保持HZ的默認值。對用戶空間,內核HZ幾乎完全隱藏,用戶HZ始終擴展為100。當用戶空間程序包含param.h,且每個報告給用戶空間的計數器都做了相應轉換。對用戶來說確切的HZ值只能通過/proc/interrupts獲得:/proc/interrupts的計數值除以/proc/uptime中報告的系統(tǒng)運行時間。對于ARM體系結構:在<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是在內核的根目錄的.config文件中定義,并沒有在makemenuconfig的配置選項中出現。Linux的\arch\arm\configs\s3c2410_defconfig文件中的定義為:#KernelFeatures#CONFIG_PREEMPTisnotsetCONFIG_NO_IDLE_HZisnotsetCONFIG_HZ=200CONFIG_AEABIisnotsetCONFIG_ARCH_DISCONTIGMEM_ENABLEisnotset所以正常情況下s3c24x0的HZ為200。這一數值在后面的實驗中可以證實。每次發(fā)生一個時鐘中斷,內核內部計數器的值就加一。這個計數器在系統(tǒng)啟動時初始化為0,因此它代表本次系統(tǒng)啟動以來的時鐘嘀噠數。這個計數器是一個64-位變量(即便在32-位的體系上)并且稱為“jiffies_64,但是驅動通常訪問jiffies變量(unsignedlong)(根據體系結構的不同:可能是jiffies_64,可能是jiffies_64的低32位)。使用jiffies是首選,因為它訪問更快,且無需在所有的體系上實現原子地訪問64-位的jiffies_64值。使用jiffies計數器這個計數器和用來讀取它的工具函數包含在<linux/jiffies.h>,通常只需包含<linux/sched.h>,它會自動放入jiffies.h。jiffies和jiffies_64必須被當作只讀變量。當需要記錄當前jiffies值(被聲明為volatile避免編譯器優(yōu)化內存讀)時,可以簡單地訪問這個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*/以下是一些簡單的工具宏及其定義:#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)用戶空間的時間表述法(structtimeval和structtimespec)與內核表述法的轉換函數:#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對于32-位處理器不是原子的,這意味著如果這個變量在你正在讀取它們時被更新你可能讀到錯誤的值。若需要訪問jiffies_64,內核有一個特別的輔助函數,為你完成適當的鎖定:#include<linux/jiffies.h>u64get_jiffies_64(void);處理器特定的寄存器若需測量非常短時間間隔或需非常高的精度,可以借助平臺依賴的資源。許多現代處理器包含一個隨時鐘周期不斷遞增的計數寄存器,他是進行高精度的時間管理任務唯一可靠的方法。最有名的計數器寄存器是TSC(timestampcounter),在x86的Pentium處理器開始引入并在之后所有的CPU中出現(包括x86_64平臺)。它是一個64-位寄存器,計數CPU的時鐘周期,可從內核和用戶空間讀取。在包含了<asm/msr.h>(一個x86-特定的頭文件,它的名子代表"machine-specificregisters")的代碼中可使用這些宏:rdtsc(low32,high32);/*原子地讀取64-位TSCrdtscl(low32);/*讀取TSC的低32位到一個32-位變量*/rdtscll(var64);/*讀64-位TSC值到一個longlong變量*//*下面的代碼行測量了指令自身的執(zhí)行時間:*/unsignedlongini,end;rdtscl(ini);rdtscl(end);printk("timelapse:%li\n",end-ini);一些其他的平臺提供相似的功能,并且內核頭文件提供一個體系無關的功能用來代替rdtsc,稱get_cycles(定義在<asm/timex.h>(由<linux/timex.h>包含)),原型如下:#include<linux/timex.h>cycles_tget_cycles(void);/*這個函數在每個平臺都有定義,但在沒有時鐘周期計數器的平臺上返回0*//*由于s3c2410系列處理器上沒有時鐘周期計數器所以get_cycles定義如下:*/typedefunsignedlongcycles_t;staticinlinecycles_tget_cycles(void)(return0;}獲取當前時間驅動一般無需知道時鐘時間(用年月日、小時、分鐘、秒來表達的時間),只對用戶程序才需要,如cron和syslogd。內核提供了一個將時鐘時間轉變?yōu)槊霐抵档暮瘮担簎nsignedlongmktime(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*/}/*這個函數將時間轉換成從1970年1月1日0小時0分0秒到你輸入的時間所經過的秒數,溢出時間為2106-02-0706:28:16。本人認為這個函數的使用應這樣:若你要計算2000-02-0706:28:16到2000-02-0906:28:16所經過的秒數:unsignedlongtime1=mktime(2000,2,7,6,28,16)-mktime(2000,2,9,6,28,16);若還要轉成jiffies,就再加上:unsignedlongtime2=time1*HZ.注意溢出的情況!*/為了處理絕對時間,<linux/time.h>導出了do_gettimeofday函數,它填充一個指向structtimeval的指針變量。絕對時間也可來自xtime變量,一個structtimespec值,為了原子地訪問它,內核提供了函數current_kernel_time。它們的精確度由硬件決定,原型是:#include<linux/time.h>voiddo_gettimeofday(structtimeval*tv);structtimespeccurrent_kernel_time(void);/*得到的數據都表示當前時間距UNIX時間基準1970-01-0100:00:00的相對時間*/以上兩個函數在ARM平臺都是通過xtime變量得到數據的。全局變量xtime:它是一個timeval結構類型的變量,用來表示當前時間距UNIX時間基準1970-01-0100:00:00的相對秒數值。結構timeval是Linux內核表示時間的一種格式(Linux內核對時間的表示有多種格式,每種格式都有不同的時間精度),其時間精度是微秒。該結構是內核表示時間時最常用的一種格式,它定義在頭文件include/linux/time.h中,如下所示:structtimeval{time_ttv_sec;/*seconds*/suseconds_ttv_usec;/*microseconds*/};其中,成員tv_sec表示當前時間距UNIX時間基準的秒數值,而成員tv_usec則表示一秒之內的微秒值,且1O00O0O>tv_usec>=0。Linux內核通過timeval結構類型的全局變量xtime來維持當前時間,該變量定義在kernel/timer.c文件中,如下所示:/*Thecurrenttime*/volatilestructtimevalxtime_attribute_((aligned(16)));但是,全局變量xtime所維持的當前時間通常是供用戶來檢索和設置的,而其他內核模塊通常很少使用它(其他內核模塊用得最多的是jiffies),因此對xtime的更新并不是一項緊迫的任務,所以這一工作通常被延遲到時鐘中斷的底半部(bottomhalf)中來進行。由于bottomhalf的執(zhí)行時間帶有不確定性,因此為了記住內核上一次更新xtime是什么時候,Linux內核定義了一個類似于jiffies的全局變量wall_jiffies,來保存內核上一次更新xtime時的jiffies值。時鐘中斷的底半部分每一次更新xtime的時侯都會將wall_jiffies更新為當時的jiffies值。全局變量wall_jiffies定義在kernel/timer.c文件中:/*jiffiesatthemostrecentupdateofwalltime*/unsignedlongwall_jiffies;原文網址:/freedom1013/archive/2007/03/13/1528310.aspx延遲執(zhí)行設備驅動常常需要延后一段時間執(zhí)行一個特定片段的代碼,常常允許硬件完成某個任務.長延遲有時,驅動需要延后執(zhí)行相對長時間,長于一個時鐘嘀噠。忙等待(盡量別用)若想延遲執(zhí)行若干個時鐘嘀噠,精度要求不高。最容易的(盡管不推薦)實現是一個監(jiān)視jiffy計數器的循環(huán)。這種忙等待實現的代碼如下:while(time_before(jiffies,j1))cpu_relax();對cpu_relex的調用將以體系相關的方式執(zhí)行,在許多系統(tǒng)中它根本不做任何事,這個方法應當明確地避免。對于ARM體系來說:#definecpu_relax()barrier()也就是說在ARM上運行忙等待相當于:while(time_before(jiffies,j1));這種忙等待嚴重地降低了系統(tǒng)性能。如果未配置內核為搶占式,這個循環(huán)在延時期間完全鎖住了處理器,計算機直到時間ji到時會完全死掉。如果運行一個可搶占的內核時會改善一點,但是忙等待在可搶占系統(tǒng)中仍然是浪費資源的。更糟的是,當進入循環(huán)時如果中斷碰巧被禁止,jiffies將不會被更新,并且while條件永遠保持真,運行一個搶占的內核也不會有幫助,唯一的解決方法是重啟。讓出處理器忙等待加重了系統(tǒng)負載,必須找出一個更好的技術:不需要CPU時釋放CPU。這可通過調用schedule函數實現(在<linux/sched.h>中聲明):while(time_before(jiffies,j1))(schedule();}在計算機空閑時運行空閑任務(進程號0,由于歷史原因也稱為swapper)可減輕處理器工作負載、降低溫度、增加壽命。超時實現延遲的最好方法應該是讓內核為我們完成相應的工作。若驅動使用一個等待隊列來等待某些其他事件,并想確保它在一個特定時間段內運行,可使用:#include<linux/wait.h>longwait_event_timeout(wait_queue_head_tq,condition,longtimeout);longwait_event_interruptible_timeout(wait_queue_head_tq,condition,longtimeout);/*這些函數在給定隊列上睡眠,但是它們在超時(以jiffies表示)到后返回。如果超時,函數返回0;如果這個進程被其他事件喚醒,則返回以jiffies表示的剩余的延遲實現;返回值從不會是負值*/為了實現進程在超時到期時被喚醒而又不等待特定事件(避免聲明和使用一個多余的等待隊列頭),內核提供了schedule_timeout函數:#include<linux/sched.h>signedlongschedule_timeout(signedlongtimeout);/*timeout是要延時的jiffies數。除非這個函數在給定的timeout流失前返回,否則返回值是0。schedule_timeout要求調用者首先設置當前的進程狀態(tài)。為獲得一個不可中斷的延遲,可使用TASK_UNINTERRUPTIBLE代替。如果你忘記改變當前進程的狀態(tài),調用schedule_time如同調用shcedule,建立一個不用的定時器。一個典型調用如下:*/set_current_state(TASK_INTERRUPTIBLE);schedule_timeout(delay);短延遲當一個設備驅動需要處理硬件的延遲(latency潛伏期),涉及到的延時通常最多幾個毫秒,在這個情況下,不應依靠時鐘嘀噠,而是內核函數ndelay,udelay和mdelay,他們分別延后執(zhí)行指定的納秒數,微秒數或者毫秒數,定義在<asm/delay.h>,原型如下:#include<linux/delay.h>voidndelay(unsignedlongnsecs);voidudelay(unsignedlongusecs);voidmdelay(unsignedlongmsecs);重要的是記住這3個延時函數是忙等待;其他任務在時間流失時不能運行。每個體系都實現udelay,但是其他的函數可能未定義;如果它們沒有定義,<linux/delay.h>提供一個缺省的基于udelay的版本。在所有的情況中,獲得的延時至少是要求的值,但可能更多。udelay的實現使用一個軟件循環(huán),它基于在啟動時計算的處理器速度和使用整數變量loos_per_jiffy確定循環(huán)次數。為避免在循環(huán)計算中整數溢出,傳遞給udelay和ndelay的值有一個上限,如果你的模塊無法加載和顯示一個未解決的符號:_bad_udelay,這意味著你調用udleay時使用太大的參數。作為一個通用的規(guī)則:若試圖延時幾千納秒,應使用udelay而不是ndelay;類似地,毫秒規(guī)模的延時應當使用mdelay完成而不是一個更細粒度的函數。有另一個方法獲得毫秒(和更長)延時而不用涉及到忙等待的方法是使用以下函數(在<linux/delay.h>中聲明):voidmsleep(unsignedintmillisecs);unsignedlongmsleep_interruptible(unsignedintmillisecs);voidssleep(unsignedintseconds)若能夠容忍比請求的更長的延時,應使用schedule_timeout,msleep或ssleep。內核定時器當需要調度一個以后發(fā)生的動作,而在到達該時間點時不阻塞當前進程,則可使用內核定時器。內核定時器用來調度一個函數在將來一個特定的時間(基于時鐘嘀噠)執(zhí)行,從而可完成各類任務。內核定時器是一個數據結構,它告訴內核在一個用戶定義的時間點使用用戶定義的參數執(zhí)行一個用戶定義的函數,函數位于<linux/timer.h>和kernel/timer.c。被調度運行的函數幾乎確定不會在注冊它們的進程在運行時運行,而是異步運行。實際上,內核定時器通常被作為一個"軟件中斷”的結果而實現。當在進程上下文之外(即在中斷上下文)中運行程序時,必須遵守下列規(guī)則:不允許訪問用戶空間;current指針在原子態(tài)沒有意義;不能進行睡眠或者調度.例如:調用kmalloc(...,GFP_KERNEL)是非法的,信號量也不能使用因為它們可能睡眠。通過調用函數in_interrupt()能夠告知是否它在中斷上下文中運行,無需參數并如果處理器當前在中斷上下文運行就返回非零。通過調用函數in_atomic()能夠告知調度是否被禁止,若調度被禁止返回非零;調度被禁止包含硬件和軟件中斷上下文以及任何持有自旋鎖的時候。在后一種情況,current可能是有效的,但是訪問用戶空間是被禁止的,因為它能導致調度發(fā)生.當使用in_interrupt()時,都應考慮是否真正該使用的是in_atomic。他們都在<asm/hardirq.h>中聲明。內核定時器的另一個重要特性是任務可以注冊它本身在后面時間重新運行,因為每個timer_list結構都會在運行前從激活的定時器鏈表中去連接,因此能夠立即鏈入其他的鏈表。一個重新注冊它自己的定時器一直運行在同一個CPU.即便在一個單處理器系統(tǒng),定時器是一個潛在的態(tài)源,這是異步運行直接結果。因此任何被定時器函數訪問的數據結構應當通過原子類型或自旋鎖被保護,避免并發(fā)訪問定時器API內核提供給驅動許多函數來聲明、注冊以及刪除內核定時器:#include<linux/timer.h>structtimer_list(structlist_headentry;unsignedlongexpires;/*期望定時器運行的絕對jiffies值,不是一個jiffies_64值,因為定時器不被期望在將來很久到時*/void(*function)(unsignedlong);/*期望調用的函數*/unsignedlongdata;/*傳遞給函數的參數,若需要在參數中傳遞多個數據項,可以將它們捆綁成單個數據結構并且將它的指針強制轉換為unsigedlong的指針傳入。這種做法在所有支持的體系上都是安全的并且在內存管理中相當普遍*/structtvec_t_base_s*base;#ifdefCONFIG_TIMER_STATSvoid*start_site;charstart_comm[16];intstart_pid;#endif};/*這個結構必須在使用前初始化,以保證所有的成員被正確建立(包括那些對調用者不透明的初始化):*/voidinit_timer(structtimer_list*timer);structtimer_listTIMER_INITIALIZER(_function,_expires,_data);/*在初始化后和調用add_timer前,可以改變3個公共成員:expires、function和data*/voidadd_timer(structtimer_list*timer);intdel_timer(structtimer_list*timer);/*在到時前禁止-的定時器*/intdel_timer_sync(structtimer_list*timer);但還保證當它返回時,定時器函數不在任何CPU上運行,以避免在SMP系統(tǒng)上競態(tài),并且在單處理器內核中和del_timer相同。這個函數應當在大部分情況下優(yōu)先考慮。如果它被從非原子上下文調用,這個函數可能睡眠,但是在其他情況下會忙等待。當持有鎖時要小心調用del_timer_sync,如果這個定時器函數試圖獲得同一個鎖,系統(tǒng)會死鎖。如果定時器函數重新注冊自己,調用者必須首先確保這個重新注冊不會發(fā)生;這通常通過設置一個〃關閉〃標志來實現,這個標志被定時器函數檢查*/intmod_timer(structtimer_list*timer,unsignedlongexpires);更新一個定時器的超時時間,常用于超時定時器。也可在正常使用add_timer時在不活動的定時器上調用mod_timer*/inttimer_pending(conststructtimer_list*timer);timer_list結構中一個不可見的成員,返回定時器是否在被調度運行*/內核定時器的實現《LDD3》介紹的比較籠統(tǒng),以后看《ULK3》的時候再細細研究。一個內核定時器還遠未完善,因為它受到jitter、硬件中斷,還有其他定時器和其他異步任務的影響。雖然一個簡單數字I/O關聯(lián)的定時器對簡單任務是足夠的,但不合適在工業(yè)環(huán)境中的生產系統(tǒng),對于這樣的任務,你將最可能需要實時內核擴展(RT-Linux).Tasklets另一個有關于定時的內核設施是tasklet。它類似內核定時器:在中斷時間運行且運行同一個CPU上,并接收一個unsignedlong參數。不同的是:無法要求在一個指定的時間執(zhí)行函數,只能簡單地要求它在以后的一個由內核選擇的時間執(zhí)行。它對于中斷處理特別有用:硬件中斷必須盡快處理,但大部分的數據管理可以延后到以后安全的時間執(zhí)行。實際上,一個tasket,就象一個內核定時器,在一個"軟中斷”的上下文中執(zhí)行(以原子模式)。軟件中斷是在使能硬件中斷時執(zhí)行異步任務的一個內核機制。tasklet以一個數據結構形式存在,使用前必須被初始化。初始化能夠通過調用一個特定函數或者通過使用某些宏定義聲明結構:#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);/*函數暫時禁止給定的tasklet被tasklet_schedule調度,直到這個tasklet被再次被enable;若這個tasklet當前在運行,這個函數忙等待直到這個tasklet退出*/voidtasklet_disable_nosync(structtasklet_struct*t);/*和tasklet_disable類似,但是tasklet可能仍然運行在另一個CPU*/voidtasklet_enable(structtasklet_struct*t);/*使能一個之前被disable的tasklet;若這個tasklet已經被調度,它會很快運行。tasklet_enable和tasklet_disable必須匹配調用,因為內核跟蹤每個tasklet的〃禁止次數〃*/voidtasklet_schedule(structtasklet_struct*t);/*調度tasklet執(zhí)行,如果tasklet在運行中被調度,它在完成后會再次運行;這保證了在其他事件被處理當中發(fā)生的事件受到應有的注意.這個做法也允許一個tasklet重新調度它自己*/voidtasklet_hi_schedule(structtasklet_struct*t);/*和tasklet_schedule類似,只是在更高優(yōu)先級執(zhí)行。當軟中斷處理運行時,它處理高優(yōu)先級tasklet在其他軟中斷之前,只有具有低響應周期要求的驅動才應使用這個函數,可避免其他軟件中斷處理引入的附加周期*/voidtasklet_kill(structtasklet_struct*t);/*確保了tasklet不會被再次調度來運行,通常當一個設備正被關閉或者模塊卸載時被調用。如果tasklet正在運行,這個函數等待直到它執(zhí)行完畢。若tasklet重新調度它自己,則必須阻止在調用tasklet_kill前它重新調度它自己,如同使用del_timer_sync*/tasklet的特點:一個tasklet能夠被禁止并且之后被重新使能;它不會執(zhí)行,直到它被使能與被禁止相同的的次數;如同定時器,一個tasklet可以注冊它自己;一個tasklet能被調度來執(zhí)行以正常的優(yōu)先級或者高優(yōu)先級;如果系統(tǒng)不在重負載下,taslet可能立刻運行,但是從不會晚于下一個時鐘嘀噠;一個tasklet可能和其他tasklet并發(fā),但是它自己是嚴格地串行的,且tasklet從不同時運行在不同處理器上,通常在調度它的同一個CPU上運行。工作隊列工作隊列類似taskets,允許內核代碼請求在將來某個時間調用一個函數,不同在于:tasklet在軟件中斷上下文中運行,所以tasklet代碼必須是原子的。而工作隊列函數在一個特殊內核進程上下文運行,有更多的靈活性,且能夠休眠。tasklet只能在最初被提交的處理器上運行,這只是工作隊列默認工作方式。內核代碼可以請求工作隊列函數被延后一個給定的時間間隔。tasklet執(zhí)行的很快,短時期,并且在原子態(tài),而工作隊列函數可能是長周期且不需要是原子的,兩個機制有它適合的情形。工作隊列有structworkqueue_struct類型,在<linux/workqueue.h>中定義。一個工作隊列必須明確的在使用前創(chuàng)建,宏為:structworkqueue_struct*create_workqueue(constchar*name);structworkqueue_struct*create_singlethread_workqueue(constchar*name);每個工作隊列有一個或多個專用的進程("內核線程”),這些進程運行提交給這個隊列的函數。若使用create項。rkqueue,就得到一個工作隊列它在系統(tǒng)的每個處理器上有一個專用的線程。在很多情況下,過多線程對系統(tǒng)性能有影響,如果單個線程就足夠則使用create_singlethread_workqueue來創(chuàng)建工作隊列。提交一個任務給一個工作隊列,在這里《LDD3》介紹的內核2.6.10和我用的新內核已經有不同了,老接口已經不能用了,編譯會出錯。這里我只講的新接口,至于老的接口我想今后內核不會再有了。從這一點我們可以看出內核發(fā)展。/*需要填充work_struct或delayed_work結構,可以在編譯時完成,宏如下:夫/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結構名稱,f是要從工作隊列被調用的函數*/DECLARE_DELAYED_WORK(n,f)/*n是聲明的delayed_work結構名稱,f是要從工作隊列被調用的函數*//*若在運行時需要建立work_struct或delayed_work結構,使用下面2個宏定義:*/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_*做更加全面的初始化結構的工作,在第一次建立結構時使用.PREPARE_*做幾乎同樣的工作,但是它不初始化用來連接work_struct或delayed_work結構到工作隊列的指針。如果這個結構已經被提交給一個工作隊列,且只需要修改該結構,則使用PREPARE_*而不是INIT_**//*有2個函數來提交工作給一個工作隊列:*/intqueue_work(structworkqueue_struct*queue,structwork_struct*work);intqueue_delayed_work(structworkqueue_struct*queue,structdelayed_work*work,unsignedlongdelay);/*每個都添加work到給定的workqueueo如果使用queue_delay_work,則實際的工作至少要經過指定的jiffies才會被執(zhí)行。這些函數若返回1則工作被成功加入到隊列;若為0,則意味著這個work已經在隊列中等待,不能再次加入*/在將來的某個時間,這個工作函數將被傳入給定的data值來調用。這個函數將在工作線程的上下文運行,因此它可以睡眠(你應當知道這個睡眠可能影響提交給同一個工作隊列的其他任務)工作函數不能訪問用戶空間,因為它在一個內核線程中運行,完全沒有對應的用戶空間來訪問。取消一個掛起的工作隊列入口項可以調用:intcancel_delayed_work(structdelayed_work*work);voidcancel_work_sync(structwork_struct*work)如果這個入口在它開始執(zhí)行前被取消,則返回非零。內核保證給定入口的執(zhí)行不會在調用cancel_delay_work后被初始化.如果cancel_delay_work返回0,但是,這個入口可能已經運行在一個不同的處理器,并且可能仍然在調用cancel_delayed_work后在運行.要絕對確保工作函數沒有在cancel_delayed_work返回0后在任何地方運行,你必須跟隨這個調用來調用:voidflush_workqueue(structworkqueue_struct*queue);在flush_workqueue返回后,沒有在這個調用前提交的函數在系統(tǒng)中任何地方運行。而cancel_work_sync會取消相應的work,但是如果這個work已經在運行那么cancel_work_sync會阻塞,直到work完成并取消相應的work。當用完一個工作隊列,可以去掉它,使用:voiddestroy_workqueue(structworkqueue_struct*queue);共享隊列在許多情況下,設備驅動不需要它自己的工作隊列。如果你只偶爾提交任務給隊列,簡單地使用內核提供的共享的默認的隊列可能更有效。若使用共享隊列,就必須明白將和其他人共享它,這意味著不應當長時間獨占隊列(不能長時間睡眠),并且可能要更長時間才能獲得處理器。使用的順序:(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);/*對于work_struct結構*/intschedule_delayed_work(&jiq_work_delay,delay);/*對于delayed_work結構*//*返回值的定義和queue_work一樣*/若需取消一個已提交給工作隊列入口項,可以使用cancel_delayed_work和cancel_work_sync,但刷新共享隊列需要一個特殊的函數:voidflush_scheduled_work(void);因為不知道誰可能使用這個隊列,因此不可能知道flush_schduled_work返回需要多長時間。ARM9s3c2440AL實驗jiq模塊:jiq實驗數據:[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等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 冀教版四年級下冊數學教案
- 農村環(huán)境整治與生態(tài)建設
- 焊接作業(yè)工藝流程標準化與優(yōu)化方案
- 生產的火災危險性分類標準
- 高一化學教案:專題第二單元第二課時乙酸酯
- 2024屆遼寧省大連海灣某中學高考仿真卷化學試卷含解析
- 2024高中物理章末質量評估四含解析新人教版選修1-1
- 2024高中語文略讀課文第8課楊振寧:合璧中西科學文化的驕子課堂練習含解析新人教版選修中外傳記蚜
- 2024高中語文第五單元散而不亂氣脈中貫自主賞析祭十二郎文學案新人教版選修中國古代詩歌散文欣賞
- 2024高中語文精讀課文二第5課1達爾文:興趣與恒心是科學發(fā)現的動力一作業(yè)含解析新人教版選修中外傳記蚜
- 施工項目環(huán)保策劃方案
- 內科護理學-第二章-呼吸系統(tǒng)疾病病人的護理試題
- 班組長培訓之品質意識培訓
- 道德經中德文對照版
- 公路工程隨機抽樣一覽表(路基路面現場測試隨機選點方法自動計算)
- 2021版中醫(yī)癥候醫(yī)保對應中醫(yī)癥候醫(yī)保2
- 2023年山東省青島市中考化學試題(含答案解析)
- 安徽華塑股份有限公司年產 4萬噸氯化石蠟項目環(huán)境影響報告書
- 世界奧林匹克數學競賽6年級試題
- 藥用植物學-課件
- 文化差異與跨文化交際課件(完整版)
評論
0/150
提交評論