《由简至繁》PPT课件.ppt_第1页
《由简至繁》PPT课件.ppt_第2页
《由简至繁》PPT课件.ppt_第3页
《由简至繁》PPT课件.ppt_第4页
《由简至繁》PPT课件.ppt_第5页
已阅读5页,还剩29页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1 Chapter6 時序 2 介紹由簡至繁 瞭解核心的時序 timing 認識目前時間 currenttime 將作業的時間點延遲到一定時間之後才開始將非同步函式 asynchronousfunction 安排到一定時間之後才開始作用 3 6 1核心的計時間隔 中斷 CPU暫停目前工作 然後執行ISR InterruptServiceRoutine 來處理中斷 CH9 計時器中斷 固定間隔觸發的中斷事件 核心依據HZ 定義在 的值來設定間隔長度 硬體平台不同 值也不同 每次發生計時器中斷 jiffies變數的值就會被遞增一次 宣告在 型別為unsignedlongvolatile 核心會確保溢位之後還能正確運作 驅動程式不必擔心jiffies溢位 4 6 1 1處理器特有的暫存器 大多數系統上 由於指令時序的不可預測性 因為有指令排程 分支預測 快取記憶體等因素 時脈計數器成為唯一可靠的精密計時工具 時脈計數器的設計隨平台而異 不一定可寫 長度也不一定是32bits或64bits 無論是否可歸零 建議不要如此 因為可以使用無號變數來計算差值 最知名的時脈計數暫存器是x86Pentium系列的TSC TimeStampCounter 為計算CPU時脈週期數的64bits的暫存器 kernel 5 6 1 1處理器特有的暫存器 Cont 量測指令本身執行時間unsignedlongini end rdtscl ini rdtscl end printk timelapse li n end ini 與平台無關 適用各種平台 的函式用來替代rdstc includecycles tget cycles void 無時脈計數 則回傳TSC暫存器低半段的值 因為可避免多暫存器操作的問題且時脈計數器主要用途為測量極短的時間如何內插組語指令 inilineassembly 將x86的rdtscl 移植到MIPS系統組語指令Movefromcoprocessor0 definerdtscl dest asm volatile mfc0 0 9 nop r dest 內插組語的語法相當有威力 但是有點複雜 特別是在那些會限定暫存器用途的平台上 x86系列 完整語法請參考gcc的說明文件 6 6 2取得目前時間 在Kernel space的程式可以從jiffies的值取得目前時間jiffies從開機到至今的時間 與驅動程式生命期無關 也不可能跨越開關機時間 驅動程式可利用jiffies的現值來估算兩事件之間的間隔時間 如mousedriver驅動程式不需要知道牆鐘時刻 wall clocktime 若真的需要靠自己處理當時的時刻 do gettimeofday 或許可派上用場 此函式並非直接告知今天是星期幾 而是將一般的秒與微秒填入一個structtimeval 原型如下 includevoiddo gettimeofday structtimeval tv 7 6 2取得目前時間 Cont 從xtime變數同樣也可取得目前時刻 但這是不被鼓勵的行為 因為無法連動 atomically 取得timevalue 結構內的tv sec與tv usec欄位值 除非暫停掉中斷 若不太講求精準度 2 2版核心提供一個快又安全的函式來取得目前時刻 voidget fast time structtimeval tv 範例jit JustInTime 模組 它不會產生裝置節點 而是直接將它取得的時刻資訊透過 proc currentime傳到user space cat proc currentime proc currentime proc currentime 8 6 3延遲執行 驅動程式通常需要拖延某段程式碼的開始執行時間 通常是為了讓硬體有足夠充裕的時間完成某些工作需要考慮的重點之一 是延遲時間是否超過一個時脈單位較長的延遲 可以利用系統時鐘來計時 較短的延遲 則通常以軟體迴圈來應付 9 6 3 1長期延遲 1 4 最簡單也是最蠢的做法 稱為忙著等待 busywaiting unsignedlongj jiffies jit delay HZ while jiffies j 發呆 因為jiffies是volatile變數 使得C編譯器會落實每次的讀取動作 不使用快取技術 在延遲期間 處理器是被鎖死的 因為這是核心裡的迴圈 排程器不會岔斷進入核心的行程 若中斷失效時進入迴圈 jiffies就不會被更新 迴圈也將無法終止 此時只能使用reset按鈕 10 6 3 1長期延遲 2 4 busy wait範例 讀取 proc jitbusy 每當它的read作業方法被呼叫一次 其內部的忙碌迴圈就會延遲一秒 如果使用ddif proc jitbusybs 1命令 就可以看到每秒讀出一個字元的效果 這種做法會嚴重拖累系統效能 因為其他行程每隔一秒才有機會執行一次 比較合理的做法是 while jiffies j schedule 但還不夠理想 倘若它是整個系統上唯一的可跑行程 它真的會動作 呼叫schedule 然後立刻被排程器選中 然後再呼叫schedule 所以說 機器負載程度將至少等於1 而idle行程將沒機會上線 由於在延遲時間 行程其實還在跑 所以它所耗的時間算在該行程上 由下列指令來證實 若在一個非常忙碌的系統上 呼叫排程器的做法 反而有可能造成驅動程式等待了超過原本預期的時間 timecat proc jitsched 11 6 3 1長期延遲 3 4 排程迴圈提供一個觀測驅動程式工作程序的速成工具 printk 之後一點點延遲 讓klogd有機會盡忠職守 以免不知如何死當 最佳的延遲方式 是要求核心代勞 核心提供兩種執行短程延遲的機制 看你的驅動程式是否要等待其他事件而定 sleep on timeout wait queue head t q unsignedlongtimeout interruptible sleep on timeout wait queue head t q unsignedlongtimeout 兩種版本都會讓行程待在指定的待命佇列裡休眠 但一定會在指定期限內返回 timeout值是要等待的jiffies個數 而非jiffies絕對值 12 6 3 1長期延遲 4 4 範例 proc jitqueuewait queue head twait init waitqueue head 但實際延遲時間 有可能略為超過你原本預期的時間 timecat proc jitqueue timecat proc jitself 13 6 3 2短期延遲 在計算非常短暫的延遲 jiffies無法達成 所以核心提供udelay 和mdelay 函式 原型如下 includevoidudelay unsignedlongusecs inlinevoidmdelay unsignedlongmsecs udelay 以當地系統的BogoMips 開機所計算出的系統常數 值來決定迴圈的圈數 其值大約是CPU時脈速的兩倍左右 mdelay 是含有udelay 的迴圈所構成兩者都是busy waiting函式 因此除非沒有其他辦法 否則應該儘量避免使用mdelay 14 6 4工作佇列 TaskQueue 驅動程式都需要的能力為不倚賴中斷機制的前提下 將某些工作安排到一段時間之後才開始執行 Linux有三種介面 分別是工作佇列 tasklet 2 3 43 以及核心計時器工作佇列和tasklet是安排工作執行時機的工具 最常被應用在interrupthandler 而核心計時器是用來將工作安排到未來的特定時間才執行 本節先說明工作佇列 然後介紹核心提供的現成工作佇列 以及如何觸發驅動程式自己產生的工作佇列 最後看看新玩意 tasklet介面 15 6 4 1工作佇列的本質 由task構成的串列 每一個工作都是以一個函式指標與一個引數的組合來表示 當工作開始跑時 它會收到一個void 引數 並傳回void 指標引數可用來傳遞資料結構給工作函式 也可以被忽略 下列結構描述引述自structtq struct structlist headlist linkedlistofactivebh s unsignedlongsync mustbeinitializedtozero void routine void functiontocall void data argumenttofunction bh代表bottomhalf interrupthandler的後半段 目前只要知道它是為了處理非同步工作所提供的機制 Ch9 16 6 4 1工作佇列的本質 Cont 核心利用sync旗標來避免將同一工作重複排在多個佇列裡 因為會破壞到next指標 另一種資料結構是task queue 它目前只是一個指向structtq struct的指標 task queue指標必須先初始化成NULL才能使用 以下是用來操作tq struct和工作佇列的工具 DECLARE TASK QUEUE name 此巨集宣告一個名為name的工作佇列 並清空它 intqueue task structtq struct task task queue list 將工作排入佇列 若工作已存在則傳回0 成功則傳回非零值 voidrun task queue task queue list 用來消化指定佇列中的累積工作 你不必直接呼叫 除非你宣告並維護自己的工作佇列 17 6 4 2工作佇列的運作原理 工作佇列何時開工 當核心空閒時 不同種類的佇列 各有不同的開工時機 工作佇列時常被當成軟體中斷的處理機制 在中斷模式執行程式碼 能力受到一些限制 必須嚴格遵守下列規矩 不容許存取user space 因為沒有行程環境 所以沒有辦法接觸到任何特定行程的user space 在中斷模式下 current指標是無效的 而且也不能使用 不能休眠 也不能要求排程 也不能使用其他可能被催眠的函數 如呼叫kmalloc將違反本規矩或使用權狀 semaphore 等 因為在等待別人讓出權狀的過程中 你會被催眠 核心程式如何得知自己是否處於中斷模式下 使用in interrupt 若傳回非零值 表示處理器正處於中斷模式 2 4版核心的工作佇列 還有一向值得注意的特性 那就是工作可以將自己排入自己原本所在的佇列裡 這種行為稱為重新排隊 18 6 4 3核心內建的工作佇列 要延遲特定工作的開始執行時間 最簡便的辦法是利用核心所維護的佇列 其中有三個可供驅動程式運用 宣告在 分別是 排程器佇列 schedulerqueue 在行程環境內運作 所以工作限制較寬鬆 在2 4版中 此佇列是以專用的kernelthread來執行的 稱為keventd 並且必須透過schedule task 來存取 計時器佇列 tq timer 由計時器時脈訊號 timertick 觸發的佇列 由於是發生在中斷期 所以此佇列的任何工作發生在中斷期 所以必須遵守中斷模式規則 即期佇列 tq immediate 被排入此佇列的工作會被盡快進行 立即佇列會在中斷期被消化完 19 20 6 4 3核心內建的工作佇列 嘗試讀取 proc jiq 檔案的行程 會被推入休眠狀態 直到緩衝區填滿 註 proc檔的緩衝區是一個分頁 才會被叫醒 修眠行程是留滯在一個簡單的待命佇列 其宣告如下 DECLARE WAIT QUEUE HEAD jiq wait 填寫緩衝區的動作 是由jiq print tq 負責 它會被排入工作佇列 並在佇列開始被消化時 輸出訊息到相關的 proc jiq 檔 初始化程序如下 structtq structjiq task 全域變數初始化歸零 jiq task routine jiq print tq jiq task data void 我們不須清除jiq task的sync和next欄位 因為編譯器會將靜態變數初始化為0 21 6 4 3 1排程器佇列 某些用途上 排程器佇列是最容易使用的 因為它並不會在中斷時期執行 所以能力限制較寬鬆 作的事也比較多 最特別的是它可以休眠 在2 4版核心 實作排程器佇列的實際工作佇列被隱藏 不讓核心其他部分接觸到 且不能直接使用queue task 必須呼叫schedule task intschedule task structtq struct task 傳回非零值代表task先前並未排入佇列 proc jiqsched是使用排程器佇列的範例檔良好的程式只能休眠非常短的週期 因為在keventd休眠期間 排程器裡的其它工作都將無法進行 timecat proc jiqsched 22 6 4 3 2計時器佇列 計時器和排程器佇列最大的差異 再於你可以直接使用計時器佇列 tq timer 由於它是在中斷時期執行 所能做的動作限制較多 計時器佇列最明顯的特性 就是它保證下次計時器中斷時 佇列裡的工作一定會被執行一次 這樣可以消除系統負載所帶來的延遲效應 讀取jiq模組的 proc jiqtimer檔案 可讓該模組將jiq task排到計時器佇列裡跑 讀取jiq模組的 proc jiqtimer檔案 可讓模組將jiq task排到計時器佇列裡跑 不同於排程器佇列之處 為這次則必須使用queue task 來將工作排入tq timer head proc jiqtimer 23 6 4 3 3即期佇列 即期佇列是透過bottom half機制來執行的 表示需要額外步驟才能使用 但核心不會隨便執行你寫好的bh 除非你將它 標示 出來 對於td immediate而言 若將工作排入該佇列 使用queue task 之後 必須立刻呼叫mark bh 否則核心有可能在你的工作排入佇列之前 就開始消化工作佇列 即期佇列是Linux系統上最快速的佇列 只要一遇到中斷 就會被立刻執行 消化其佇列的時機有二 一是由排程器觸發 二是在行程從系統呼叫返回之後那一瞬間 24 6 4 3 3即期佇列 Cont 讀取 proc jiqimmed很顯然地 即期佇列不能用來延遲工作的執行 畢竟它是 即期 佇列 這項特性使它成為interrupthandler的重要資源 因為它可讓interrupthandler用來安排來不及在中斷時期內執行完畢的工作 如 接收網路封包 注意 排在即期佇列裡的工作 不應該有重新排隊的行為 這樣做並沒有好處 反而還有可能鎖死系統 因為某些平台核心的即期佇列會一直跑到清空為止 而重新排隊將使得佇列沒有清空的機會 head proc jiqimmed 25 6 4 4自製的工作佇列 驅動程式有權宣告自己專用的新工作佇列 一個或多個 不同於核心所提供的工作佇列 核心不會自動執行驅動程式產生的佇列 所以程式設計師必須另外安排觸發動作 以下巨集用來宣告自製佇列 此巨集展開後會成為一般的變數宣告 所以應該宣告在程式開頭DECLARE TASK QUEUE tq custom 之後就可以使用正常函式來操作工作佇列 通常第一步是 queue task 還需要另外再核心提供的現成工作佇列註冊一個函式 由該函式觸發你的自製佇列 26 6 4 5Tasklet 1 3 在2 4版正式發行之前 開發人員增加了一種新機制來執行延期的核心工作 此機制稱為tasklets 並且成為bottom half工作的最佳選擇 事實上 bottom half本身現在就是做成tasklet的形式 Tasklet如同工作佇列 不管被排程幾次 tasklet也是只跑一次而已 在SMP系統上 tasklet可以與其他不同的tasklet並行運作 哪個CPU安排的tasklet 就由哪一個CPU負責執行 這不僅簡化了系統的快取機制 同時也獲得更高的效能 每一個tasklet都有一個專屬的函式 當tasklet到了應該被執行的時間 該函式就會被呼叫 此函式只能有一個unsignedlong引數 不過將long引數鑄型 cast 成指標型別 在任何支援linux的平台上都是可行而安全的 甚至是記憶管理常用的技巧 Ch13 27 6 4 5Tasklet 2 3 對於tasklet的軟體支援 都收納在 而tasklet本身必須以下列方式之一宣告 DECLARE TASKLET name function data 以指定的name宣告一個tasklet 在tasklet要被執行時 指定的function會被呼叫 並且會收到一個unsignedlongdata值 DECLARE TASKLET DISABLED name function data 同上式 但是其初始狀態是失效 disabled 這表示它會參與排程 但是不會被執行 除非你在未來的某時間點讓它生效 jiq模組以下列方式宣告其tasklet voidjiq print tasklet unsignedlong DECLARE TASKLET jiq tasklet jiq print tasklet unsignedlong 28 6 4 5Tasklet 3 3 proc jiqtasklet指令注意到tasklet總是由同一個CPU負責執行 即使另一個CPU空閒時 核心的子系統還提供了一些輔助工具 幫助你進行更進階的應用 voidtasklet disable structtasklet struct t 將tasklet暫時失效 但已加入排程則會繼續參與 但不會被選中 除非你在未來的某時間點讓它生效 voidtasklet enable structtasklet struct t 使失效的tasklet重新生效 對於已納入排程系統的tasklet 將盡快恢復執行 voidtasklet kill structtasklet struct t tasklet kill 會將tasklet從任何他所在的佇列中抽離 為了那些解決會主動重新排隊的tasklet 此函數會等待要開始執行tasklet之前 才將他抽離佇列 不過 要是指定的tasklet不是可執行狀態而且不會重新排隊 則此tasklet kill 可能會當掉 且不可在中斷時期呼叫它 head proc jiqtasklet 29 6 5核心計時器 1 3 它是核心內維持時序的終極資源 可用來安排一個函式 timeouthandler 在未來的某特定時間才開始執行 而且只會執行一次 這個特性跟工作佇列一樣 計時器用法相當容易 你的工作函式只要註冊一次 核心就會在計時器到期時呼叫該函式 核心的計時器群是組織成一個雙向鏈結串列 可隨意增加 注意 期限值是一個絕對值 取jiffies的現值在加上你想要延遲的間隔 初始化timer list後 就可用add timer 將該結構安插到一個有序串列 大約每10ms會被輪詢一次 30 6 5核心計時器 2 3 以下是來操作計時器的工具函式 voidinit timer structtimer list timer 這是inline函式 用於初始化timer list結構 它將prev和next指標歸零 SMP running歸零 voidadd timer structtimer list timer 將指定計時器安插到全域性的活動計時器串列intmod timer structtimer list timer unsignedlongexpires 修改計時器時限 在返回瞬間開始生效 intdel timer structtimer list timer 若到期之前 會將計時器移出串列 若是在到期之後 核心會自動將計時器排出串列 intdel timer sync structtimer lis

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论