TLB重新整理的深入理解(續)

2021-08-25 01:30:34 字數 3576 閱讀 1304

有兩點注意:1.有些體系結構上tlb不命中和tlb實效是兩回事,不命中的意思是tlb表項的有效位為1,存在位為0,而tlb無效是有效位為0,存在位無意義,當tlb無效的時候,預取邏輯就要查頁表,並且不把結果回存進tlb,只有當使用者實際訪問該位址(而不是預取)時才將查表結果存入tlb,當tlb不命中的時候,還是查頁表,但是此時會將結果存入tlb;2.x86的tlb查表如果不命中的話,就會將查頁表的結果直接輸出,而別的一些體系結構不命中 tlb的時候,會先將位址對映存入tlb,然後從tlb輸出。我很想說清這個問題,再看看下面的解釋吧,我相信這下沒有什麼問題了。

tlb命中是得到了實體地址,但是cpu是不會直接將這個實體地址放到位址匯流排去內訪問資料的,而是先會查一下cache裡面有沒有這個實體地址對應的資料,如果沒有的話再阻塞然後去物理記憶體取,這個確實有一些複雜,我想了好久都沒有想出乙個例子講這些闡述清楚,有時自己也能被繞進去,不管怎樣我還是想盡力去說清,本來表達能力就不好更應該練練了,呵呵。

舉個終結例子吧,tlb中存的是頁表快取,其實是「虛擬位址頁表項」對,如果tlb命中,那麼就會從tlb對應的「頁表項」中取得乙個實體地址,然後取這個實體地址的資料,cpu會先從cache中取,如果沒有再訪問記憶體,這時就不再需要通過頁目錄-〉頁表的方式訪問記憶體了,而是直接用從tlb中得到的實體地址訪問記憶體,接下來我試圖給個終結者場景:cpu預取邏輯預取乙個位址的資料,但是這個位址在tlb沒有命中,於是cpu預取邏輯開始按照頁目錄->頁表的方式進行掃瞄,當然它要訪問頁目錄,頁表,這時它要訪問頁表了,加上位址偏移得到頁表項,mmu先看看cache裡面有沒有頁表項,有還是沒有呢?沒有,沒有頁表項的資料,於是 cache要從記憶體取回頁表項實體地址對應的資料,其實就是乙個頁表項pte,於是阻塞將頁表項讀到cache,在cache取頁表項的路上,頁表項所在的頁表被釋放並重分配為其他用途並將這個要取得的頁表項的g位設定1,此時cache取訊號到了,取回的頁表項將是無效的頁表項,帶有g位,並把它存到 tlb,並且將永存於tlb(因為g),然後從這個頁表獲得的資料也將是無效資料,比如本來需要取乙個「add」指令,結果取回的這個帶有g位的頁表項對應的高20位指示的位址裡面卻是「****」,於是等到執行這個****的時候就真的****了...

希望這下說清楚了,這個場景是乙個完整的訪問流程。

載入cr3就重新整理了tlb,之後預取邏輯會重新開始頁目錄-〉頁表掃瞄,注意,flush tlb或者load cr3將時間分為了三個部分,分別為重新整理前,重新整理時,重新整理後,flush之前的部分保證頁表還沒有被釋放,因此不可能出現頁表被重分配然後設定g位,然後存到tlb不被重新整理影響的情況;重新整理時,所有的tlb將失效,至於已經預取的指令,則由cpu執行邏輯和分支**邏輯判斷,反正tlb中的東西都無效了;重新整理後,因為tlb無效了,因此預取邏輯必須按照頁目錄-〉頁表方式取得實體地址並且可能的情況下添到tlb,可是此時的頁表項或者頁中間目錄項已經設定為「頁不在記憶體」了,也就是最低位已經是0了,那麼預取將不再進行。這裡關鍵是要使得已經給出的 "cache從記憶體讀頁表項的請求"失效, 而回滾從頭來一次。

關於linux輸入子系統的通知

今天早上開啟郵件列表,發現有個人提交了乙個補丁,是「input notifier support」大致看了一下,感覺不錯,到我驚嘆的時候,我只是一味的覺得核心的開發者的思維就是開闊,**有需求,**就有**,那麼在我提出相反觀點之前,我還是先說一下這個補丁吧。

這個補丁主要解決input子系統和別的系統通訊問題,現在的問題是,input子系統設計的非常好,想想我們使用計算機的方式,就是給計算機乙個輸入,然後等待計算機的輸出,input子系統涵蓋了計算機輸入,而輸出可以以可視的形式顯示在終端,又可以驅動一些裝置,因此計算機基本就是三部分,輸入,計算,輸出,而輸入很有必要單獨成為乙個子系統,可見input子系統的重要,它有必要在有動作的時候進行一些通知,因為輸入動作雖然是單純的輸入,但是有時候有必要告訴別的系統有了輸入,比如按下某些鍵盤的按鍵的時候,led燈必須閃爍,而再按下或者放開的時候,該燈就要熄滅,人們雖然很注意輸入過後的輸出,另外還對輸入事件的確認很在乎,因此這個補丁就提出來了,其實補丁提出來之前,輸入的某些確認照樣可以被感知,但是方式卻很土,比如在按下乙個按鍵的時候,特定的**會寫死到相關的按鍵處理裡面,一改則大改,很不方便。這個哥們提出了乙個補丁,用通知鏈的技術解決了這個問題,通知鏈技術可以用統一的方式通知一切相關的感興趣的實體,這個補丁就是註冊了乙個通知鏈,然後在特定的輸入事件觸發時,通知這個鍊子上的所有感興趣者就可以了。

void input_event(struct input_dev *dev,

add_input_randomness(type, code, value);

input_handle_event(dev, type, code, value);

spin_unlock_irqrestore(&dev->event_lock, flags);

+ if (type == ev_key)

+ input_notifier_call_chain(value, &code);

+int input_register_client(struct notifier_block *nb)

+ return blocking_notifier_chain_register(&input_notifier_list, nb);

+int input_notifier_call_chain(unsigned long val, void *v)

+ return blocking_notifier_call_chain(&input_notifier_list, val, v);

就是以上那麼簡單,需要被輸入事件通知的只需要註冊乙個notifier_block就行了,就是呼叫input_register_client就可以,在輸入事件觸發的時候就呼叫input_notifier_call_chain來觸發被通知的事件。這樣就用通知鏈將不相干的模組聯絡了起來,如果按照這個補丁的思想,核心中任何的模組都能通過通知鏈聯絡起來,可是這樣可以嗎?核心**早就實現了通知鏈,為何到了2.6.29了還沒有用通知鏈聯絡很多東西呢?我覺得可能核心壓根就不想這麼做,因為核心沒有必要耦合這些模組,linux十分反感這種高耦合的東西,linux總是將很急迫的兩個模組直接耦合,不是很急迫的總是通過乙個二傳手,最好的二傳手就是使用者空間,一般都是通過netlink將事件導到使用者空間,然後使用者空間少處理後在傳入核心的另外乙個模組,對於輸入的通知,到底該怎麼做呢?

我個人認為這個補丁雖然解決了問題,但是卻開了乙個不好的頭,以後核心中將大量充斥這種強耦合的**,其實這裡不能叫做強耦合,因為通知鏈本身就是乙個粘合層,可是要知道通知通知鏈的實體是同步進行的,這勢必要增加處理時間,如果換成非同步方式通知,那麼就要修改核心通知鏈架構,而且我們看一下這個輸入通知,如果僅僅是反饋乙個輸入事件比如閃爍乙個燈,那麼根本沒有必要用這麼耗時的處理機制,於是我提出我的改進方式。

這個哥們提出的輸入通知思想非常好,可是我不太贊同他的實現,我的實現有兩種,第一就是用netlink通知使用者空間,然後使用者空間再用netlink通知需要被通知的實體,使用者空間啟動乙個守護程序充當這個二傳手,還有一種方式就是實現非同步方式的通知鏈,這樣的話通知鏈解除了兩個通訊實體之間的耦合,而非同步的通知鏈技術不再延遲通知的執行,我準備明天開始寫乙個非同步通知鏈補丁,然後提交到核心maillist。

附:/dev目錄和/sys目錄

這連個目錄都和裝置有關係,/sys用於裝置的是管理,/dev則是抽象出來的真實地可以操縱的裝置,sysfs就是核心的裝置模型的kobject組合成的提供給使用者空間的管理實體,而/dev則是實際的裝置

深入理解C語言 深入理解指標

關於指標,其是c語言的重點,c語言學的好壞,其實就是指標學的好壞。其實指標並不複雜,學習指標,要正確的理解指標。指標也是一種變數,占有記憶體空間,用來儲存記憶體位址 指標就是告訴編譯器,開闢4個位元組的儲存空間 32位系統 無論是幾級指標都是一樣的 p操作記憶體 在指標宣告時,號表示所宣告的變數為指...

mysql 索引深入理解 深入理解MySql的索引

為什麼索引能提高查詢速度 先從 mysql的基本儲存結構說起 mysql的基本儲存結構是頁 記錄都存在頁裡邊 各個資料頁可以組成乙個雙向鍊錶每個資料頁中的記錄又可以組成乙個單向鍊錶 每個資料頁都會為儲存在它裡邊兒的記錄生成乙個頁目錄,在通過主鍵查詢某條記錄的時候可以在頁目錄中使用二分法快速定位到對應...

深入理解C語言 深入理解指標

關於指標,其是c語言的重點,c語言學的好壞,其實就是指標學的好壞。其實指標並不複雜,學習指標,要正確的理解指標。指標也是一種變數,占有記憶體空間,用來儲存記憶體位址 指標就是告訴編譯器,開闢4個位元組的儲存空間 32位系統 無論是幾級指標都是一樣的 p操作記憶體 在指標宣告時,號表示所宣告的變數為指...