日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

【深度】韦东山:一文看尽 linux对中断处理的前世今生

發布時間:2023/12/20 linux 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【深度】韦东山:一文看尽 linux对中断处理的前世今生 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

作者:韋東山

前言:

本文,4200字,研究代碼花了一天,寫出來花了一天;

錄視頻估計又得花半天;

真懷念以前簡單粗暴的生活啊:

拿起話筒就錄視頻,

先畫好圖?那是不需要的

文檔?那是不存在的

真是灑脫.....

現在,要寫文檔,又要畫流程圖,十幾、二十分鐘的視頻,

真是漚心瀝血做出來的,

各位,別浪費了,歡迎享受

韋東山老師正在錄本文配套的視頻,明天發布。咱們先預習。

分為7點:

  • Linux對中斷的擴展:硬件中斷,軟件中斷

  • 中斷處理原則1:不能嵌套

  • 中斷處理原則2:越快越好

  • 要處理的事情實在太多:拆分為:上半部,下半部

  • 下半部的事情耗時不是太長:tasklet

  • 下半部要做的事情太多并且很復雜:工作隊列

  • 新技術:threaded irq

  • 從2005年我接觸Linux到現在15年了,Linux中斷系統的變化并不大。比較重要的就是引入了threaded irq:使用內核線程來處理中斷。

    Linux系統中有硬件中斷,也有軟件中斷。

    對硬件中斷的處理有2個原則:不能嵌套,越快越好。

    參考資料:

    https://blog.csdn.net/myarrow/article/details/9287169

    01

    Linux對中斷的擴展:硬件中斷、軟件中斷

    Linux系統把中斷的意義擴展了,對于按鍵中斷等硬件產生的中斷,稱之為“硬件中斷”(hard irq)。每個硬件中斷都有對應的處理函數,比如按鍵中斷、網卡中斷的處理函數肯定不一樣。

    為方便理解,你可以先認為對硬件中斷的處理是用數組來實現的,數組里存放的是函數指針:

    注意:上圖是簡化的,Linux中這個數組復雜多了。當發生A中斷時,對應的irq_function_A函數被調用。硬件導致該函數被調用。相對的,還可以人為地制造中斷:軟件中斷(soft irq),如下圖所示:

    注意:上圖是簡化的,Linux中這個數組復雜多了。

    問題來了:

    a. 軟件中斷何時生產?

    由軟件決定,對于X號軟件中斷,只需要把它的flag設置為1就表示發生了該中斷。

    b. 軟件中斷何時處理?

    軟件中斷嘛,并不是那么十萬火急,有空再處理它好了。

    什么時候有空?不能讓它一直等吧?

    Linux系統中,各種硬件中斷頻繁發生,至少定時器中斷每10ms發生一次,那取個巧?

    在處理寫硬件中斷后,再去處理軟件中斷?就這么辦!

    有哪些軟件中斷?

    查內核源碼include/linux/interrupt.h

    怎么觸發軟件中斷?最核心的函數是raise_softirq,簡單地理解就是設置softirq_veq[nr]的標記位:

    怎么設置軟件中斷的處理函數:

    后面講到的中斷下半部tasklet就是使用軟件中斷實現的。

    02

    中斷處理原則1:不能嵌套

    官方資料:中斷處理不能嵌套

    https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e58aa3d2d0cc

    中斷處理函數需要調用C函數,這就需要用到棧。

    中斷A正在處理的過程中,假設又發生了中斷B,那么在棧里要保存A的現場,然后處理B。

    在處理B的過程中又發生了中斷C,那么在棧里要保存B的現場,然后處理C。

    如果中斷嵌套突然暴發,那么棧將越來越大,棧終將耗盡。

    所以,為了防止這種情況發生,也是為了簡單化中斷的處理,在Linux系統上中斷無法嵌套:即當前中斷A沒處理完之前,不會響應另一個中斷B(即使它的優先級更高)。

    03

    中斷處理原則2:越快越好

    媽媽在家中照顧小孩時,門鈴響起,她開門取快遞:這就是中斷的處理。她取個快遞敢花上半天嗎?不怕小孩出意外嗎?

    同理,在Linux系統中,中斷的處理也是越快越好。

    在單芯片系統中,假設中斷處理很慢,那應用程序在這段時間內就無法執行:系統顯得很遲頓。

    在SMP系統中,假設中斷處理很慢,那么正在處理這個中斷的CPU上的其他線程也無法執行。

    在中斷的處理過程中,該CPU是不能進行進程調度的,所以中斷的處理要越快越好,盡早讓其他中斷能被處理──進程調度靠定時器中斷來實現。

    在Linux系統中使用中斷是挺簡單的,為某個中斷irq注冊中斷處理函數handler,可以使用request_irq函數:

    在handler函數中,代碼盡可能高效。

    但是,處理某個中斷要做的事情就是很多,沒辦法加快。比如對于按鍵中斷,我們需要等待幾十毫秒消除機械抖動。難道要在handler中等待嗎?對于計算機來說,這可是一個段很長的時間。

    怎么辦?

    04

    要處理的事情實在太多,拆分為:上半部、下半部

    當一個中斷要耗費很多時間來處理時,它的壞處是:在這段時間內,其他中斷無法被處理。換句話說,在這段時間內,系統是關中斷的。

    如果某個中斷就是要做那么多事,我們能不能把它拆分成兩部分:緊急的、不緊急的?

    在handler函數里只做緊急的事,然后就重新開中斷,讓系統得以正常運行;那些不緊急的事,以后再處理,處理時是開中斷的。

    中斷下半部的實現有很多種方法,講2種主要的:tasklet(小任務)、work queue(工作隊列)。


    05

    下半部要做的事情耗時不是太長:tasklet

    假設我們把中斷分為上半部、下半部。發生中斷時,上半部下半部的代碼何時、如何被調用?

    當下半部比較耗時但是能忍受,并且它的處理比較簡單時,可以用tasklet來處理下半部。tasklet是使用軟件中斷來實現。

    寫字太多,不如貼代碼,代碼一目了然:

    使用流程圖簡化一下:

    假設硬件中斷A的上半部函數為irq_top_half_A,下半部為irq_bottom_half_A。

    使用情景化的分析,才能理解上述代碼的精華。

    a. 硬件中斷A處理過程中,沒有其他中斷發生:

    一開始,preempt_count = 0;

    上述流程圖①~⑨依次執行,上半部、下半部的代碼各執行一次。

    b. 硬件中斷A處理過程中,又再次發生了中斷A:

    一開始,preempt_count = 0;

    執行到第⑥時,一開中斷后,中斷A又再次使得CPU跳到中斷向量表。

    注意

    這時preempt_count等于1,并且中斷下半部的代碼并未執行。

    CPU又從①開始再次執行中斷A的上半部代碼:

    在第①步preempt_count等于2;

    在第③步preempt_count等于1;

    在第④步發現preempt_count等于1,所以直接結束當前第2次中斷的處理;

    注意

    重點來了,第2次中斷發生后,打斷了第一次中斷的第⑦步處理。當第2次中斷處理完畢,CPU會繼續去執行第⑦步。

    可以看到,發生2次硬件中斷A時,它的上半部代碼執行了2次,但是下半部代碼只執行了一次。

    所以,同一個中斷的上半部、下半部,在執行時是多對一的關系。

    c. 硬件中斷A處理過程中,又再次發生了中斷B:

    一開始,preempt_count = 0;

    執行到第⑥時,一開中斷后,中斷B又再次使得CPU跳到中斷向量表。

    注意

    這時preempt_count等于1,并且中斷A下半部的代碼并未執行。

    CPU又從①開始再次執行中斷B的上半部代碼:

    在第①步preempt_count等于2;

    在第③步preempt_count等于1;

    在第④步發現preempt_count等于1,所以直接結束當前第2次中斷的處理;

    注意

    重點來了,第2次中斷發生后,打斷了第一次中斷A的第⑦步處理。當第2次中斷B處理完畢,CPU會繼續去執行第⑦步。

    在第⑦步里,它會去執行中斷A的下半部,也會去執行中斷B的下半部。

    所以,多個中斷的下半部,是匯集在一起處理的。

    總結

    a. 中斷的處理可以分為上半部,下半部

    b. 中斷上半部,用來處理緊急的事,它是在關中斷的狀態下執行的

    c. 中斷下半部,用來處理耗時的、不那么緊急的事,它是在開中斷的狀態下執行的

    d. 中斷下半部執行時,有可能會被多次打斷,有可能會再次發生同一個中斷

    e. 中斷上半部執行完后,觸發中斷下半部的處理

    f. 中斷上半部、下半部的執行過程中,不能休眠:中斷休眠的話,以后誰來調度進程啊?

    06

    下半部要做的事情太多并且很復雜:工作隊列

    在中斷下半部的執行過程中,雖然是開中斷的,期間可以處理各類中斷。但是畢竟整個中斷的處理還沒走完,這期間APP是無法執行的。

    假設下半部要執行1、2分鐘,在這1、2分鐘里APP都是無法響應的。

    這誰受得了?

    所以,如果中斷要做的事情實在太耗時,那就不能用中斷下半部來做,而應該用內核線程來做:在中斷上半部喚醒內核線程。內核線程和APP都一樣競爭執行,APP有機會執行,系統不會卡頓。

    這個內核線程是系統幫我們創建的,一般是kworker線程,內核中有很多這樣的線程:

    kworker線程要去“工作隊列”(work queue)上取出一個一個“工作”(work),來執行它里面的函數。

    那我們怎么使用work、work queue呢?

    a. 創建work:

    你得先寫出一個函數,然后用這個函數填充一個work結構體。比如:

    b. 要執行這個函數時,把work提交給work queue就可以了:

    上述函數會把work提供給系統默認的work queue:system_wq,它是一個隊列。

    c. 誰來執行work中的函數?

    不用我們管,schedule_work函數不僅僅是把work放入隊列,還會把kworker線程喚醒。此線程搶到時間運行時,它就會從隊列中取出work,執行里面的函數。

    d. 誰把work提交給work queue?

    在中斷場景中,可以在中斷上半部調用schedule_work函數。

    總結

    a. 很耗時的中斷處理,應該放到線程里去

    b. 可以使用work、work queue

    c. 在中斷上半部調用schedule_work函數,觸發work的處理

    d. 既然是在線程中運行,那對應的函數可以休眠。


    07

    新技術:threaded irq

    使用線程來處理中斷,并不是什么新鮮事。使用work就可以實現,但是需要定義work、調用schedule_work,好麻煩啊。

    太懶了太懶了,就這2步你們都不愿意做。

    好,內核是為懶人服務的,再殺出一個函數:

    你可以只提供thread_fn,系統會為這個函數創建一個內核線程。發生中斷時,內核線程就會執行這個函數。

    說你懶是開玩笑,內核開發者也不會那么在乎懶人。

    以前用work來線程化的處理內核,一個worker線程只能由一個CPU執行,多個中斷的work都由同一個worker線程來處理,在單CPU系統中也只能忍著了。但是在SMP系統中,明明有那么多CPU空著,你偏偏讓多個中斷擠在這個CPU上?

    新技術threaded irq,為每一個中斷都創建一個內核線程;多個中斷的內核線程可以分配到多個CPU上執行,這提高了效率。

    ☆ END ☆

    掃碼或長按關注

    回復「?加群?」進入技術群聊

    創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

    總結

    以上是生活随笔為你收集整理的【深度】韦东山:一文看尽 linux对中断处理的前世今生的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。