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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

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

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

作者:韋東山

前言:

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

錄視頻估計(jì)又得花半天;

真懷念以前簡(jiǎn)單粗暴的生活啊:

拿起話筒就錄視頻,

先畫好圖?那是不需要的

文檔?那是不存在的

真是灑脫.....

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

真是漚心瀝血做出來的,

各位,別浪費(fèi)了,歡迎享受

韋東山老師正在錄本文配套的視頻,明天發(fā)布。咱們先預(yù)習(xí)。

分為7點(diǎn):

  • Linux對(duì)中斷的擴(kuò)展:硬件中斷,軟件中斷

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

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

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

  • 下半部的事情耗時(shí)不是太長(zhǎng):tasklet

  • 下半部要做的事情太多并且很復(fù)雜:工作隊(duì)列

  • 新技術(shù):threaded irq

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

    Linux系統(tǒng)中有硬件中斷,也有軟件中斷。

    對(duì)硬件中斷的處理有2個(gè)原則:不能嵌套,越快越好。

    參考資料:

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

    01

    Linux對(duì)中斷的擴(kuò)展:硬件中斷、軟件中斷

    Linux系統(tǒng)把中斷的意義擴(kuò)展了,對(duì)于按鍵中斷等硬件產(chǎn)生的中斷,稱之為“硬件中斷”(hard irq)。每個(gè)硬件中斷都有對(duì)應(yīng)的處理函數(shù),比如按鍵中斷、網(wǎng)卡中斷的處理函數(shù)肯定不一樣。

    為方便理解,你可以先認(rèn)為對(duì)硬件中斷的處理是用數(shù)組來實(shí)現(xiàn)的,數(shù)組里存放的是函數(shù)指針:

    注意:上圖是簡(jiǎn)化的,Linux中這個(gè)數(shù)組復(fù)雜多了。當(dāng)發(fā)生A中斷時(shí),對(duì)應(yīng)的irq_function_A函數(shù)被調(diào)用。硬件導(dǎo)致該函數(shù)被調(diào)用。相對(duì)的,還可以人為地制造中斷:軟件中斷(soft irq),如下圖所示:

    注意:上圖是簡(jiǎn)化的,Linux中這個(gè)數(shù)組復(fù)雜多了。

    問題來了:

    a. 軟件中斷何時(shí)生產(chǎn)?

    由軟件決定,對(duì)于X號(hào)軟件中斷,只需要把它的flag設(shè)置為1就表示發(fā)生了該中斷。

    b. 軟件中斷何時(shí)處理?

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

    什么時(shí)候有空?不能讓它一直等吧?

    Linux系統(tǒng)中,各種硬件中斷頻繁發(fā)生,至少定時(shí)器中斷每10ms發(fā)生一次,那取個(gè)巧?

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

    有哪些軟件中斷?

    查內(nèi)核源碼include/linux/interrupt.h

    怎么觸發(fā)軟件中斷?最核心的函數(shù)是raise_softirq,簡(jiǎn)單地理解就是設(shè)置softirq_veq[nr]的標(biāo)記位:

    怎么設(shè)置軟件中斷的處理函數(shù):

    后面講到的中斷下半部tasklet就是使用軟件中斷實(shí)現(xiàn)的。

    02

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

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

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

    中斷處理函數(shù)需要調(diào)用C函數(shù),這就需要用到棧。

    中斷A正在處理的過程中,假設(shè)又發(fā)生了中斷B,那么在棧里要保存A的現(xiàn)場(chǎng),然后處理B。

    在處理B的過程中又發(fā)生了中斷C,那么在棧里要保存B的現(xiàn)場(chǎng),然后處理C。

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

    所以,為了防止這種情況發(fā)生,也是為了簡(jiǎn)單化中斷的處理,在Linux系統(tǒng)上中斷無法嵌套:即當(dāng)前中斷A沒處理完之前,不會(huì)響應(yīng)另一個(gè)中斷B(即使它的優(yōu)先級(jí)更高)。

    03

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

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

    同理,在Linux系統(tǒng)中,中斷的處理也是越快越好。

    在單芯片系統(tǒng)中,假設(shè)中斷處理很慢,那應(yīng)用程序在這段時(shí)間內(nèi)就無法執(zhí)行:系統(tǒng)顯得很遲頓。

    在SMP系統(tǒng)中,假設(shè)中斷處理很慢,那么正在處理這個(gè)中斷的CPU上的其他線程也無法執(zhí)行。

    在中斷的處理過程中,該CPU是不能進(jìn)行進(jìn)程調(diào)度的,所以中斷的處理要越快越好,盡早讓其他中斷能被處理──進(jìn)程調(diào)度靠定時(shí)器中斷來實(shí)現(xiàn)。

    在Linux系統(tǒng)中使用中斷是挺簡(jiǎn)單的,為某個(gè)中斷irq注冊(cè)中斷處理函數(shù)handler,可以使用request_irq函數(shù):

    在handler函數(shù)中,代碼盡可能高效。

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

    怎么辦?

    04

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

    當(dāng)一個(gè)中斷要耗費(fèi)很多時(shí)間來處理時(shí),它的壞處是:在這段時(shí)間內(nèi),其他中斷無法被處理。換句話說,在這段時(shí)間內(nèi),系統(tǒng)是關(guān)中斷的。

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

    在handler函數(shù)里只做緊急的事,然后就重新開中斷,讓系統(tǒng)得以正常運(yùn)行;那些不緊急的事,以后再處理,處理時(shí)是開中斷的。

    中斷下半部的實(shí)現(xiàn)有很多種方法,講2種主要的:tasklet(小任務(wù))、work queue(工作隊(duì)列)。


    05

    下半部要做的事情耗時(shí)不是太長(zhǎng):tasklet

    假設(shè)我們把中斷分為上半部、下半部。發(fā)生中斷時(shí),上半部下半部的代碼何時(shí)、如何被調(diào)用?

    當(dāng)下半部比較耗時(shí)但是能忍受,并且它的處理比較簡(jiǎn)單時(shí),可以用tasklet來處理下半部。tasklet是使用軟件中斷來實(shí)現(xiàn)。

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

    使用流程圖簡(jiǎn)化一下:

    假設(shè)硬件中斷A的上半部函數(shù)為irq_top_half_A,下半部為irq_bottom_half_A。

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

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

    一開始,preempt_count = 0;

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

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

    一開始,preempt_count = 0;

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

    注意

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

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

    在第①步preempt_count等于2;

    在第③步preempt_count等于1;

    在第④步發(fā)現(xiàn)preempt_count等于1,所以直接結(jié)束當(dāng)前第2次中斷的處理;

    注意

    重點(diǎn)來了,第2次中斷發(fā)生后,打斷了第一次中斷的第⑦步處理。當(dāng)?shù)?次中斷處理完畢,CPU會(huì)繼續(xù)去執(zhí)行第⑦步。

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

    所以,同一個(gè)中斷的上半部、下半部,在執(zhí)行時(shí)是多對(duì)一的關(guān)系。

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

    一開始,preempt_count = 0;

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

    注意

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

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

    在第①步preempt_count等于2;

    在第③步preempt_count等于1;

    在第④步發(fā)現(xiàn)preempt_count等于1,所以直接結(jié)束當(dāng)前第2次中斷的處理;

    注意

    重點(diǎn)來了,第2次中斷發(fā)生后,打斷了第一次中斷A的第⑦步處理。當(dāng)?shù)?次中斷B處理完畢,CPU會(huì)繼續(xù)去執(zhí)行第⑦步。

    在第⑦步里,它會(huì)去執(zhí)行中斷A的下半部,也會(huì)去執(zhí)行中斷B的下半部。

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

    總結(jié)

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

    b. 中斷上半部,用來處理緊急的事,它是在關(guān)中斷的狀態(tài)下執(zhí)行的

    c. 中斷下半部,用來處理耗時(shí)的、不那么緊急的事,它是在開中斷的狀態(tài)下執(zhí)行的

    d. 中斷下半部執(zhí)行時(shí),有可能會(huì)被多次打斷,有可能會(huì)再次發(fā)生同一個(gè)中斷

    e. 中斷上半部執(zhí)行完后,觸發(fā)中斷下半部的處理

    f. 中斷上半部、下半部的執(zhí)行過程中,不能休眠:中斷休眠的話,以后誰(shuí)來調(diào)度進(jìn)程啊?

    06

    下半部要做的事情太多并且很復(fù)雜:工作隊(duì)列

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

    假設(shè)下半部要執(zhí)行1、2分鐘,在這1、2分鐘里APP都是無法響應(yīng)的。

    這誰(shuí)受得了?

    所以,如果中斷要做的事情實(shí)在太耗時(shí),那就不能用中斷下半部來做,而應(yīng)該用內(nèi)核線程來做:在中斷上半部喚醒內(nèi)核線程。內(nèi)核線程和APP都一樣競(jìng)爭(zhēng)執(zhí)行,APP有機(jī)會(huì)執(zhí)行,系統(tǒng)不會(huì)卡頓。

    這個(gè)內(nèi)核線程是系統(tǒng)幫我們創(chuàng)建的,一般是kworker線程,內(nèi)核中有很多這樣的線程:

    kworker線程要去“工作隊(duì)列”(work queue)上取出一個(gè)一個(gè)“工作”(work),來執(zhí)行它里面的函數(shù)。

    那我們?cè)趺词褂脀ork、work queue呢?

    a. 創(chuàng)建work:

    你得先寫出一個(gè)函數(shù),然后用這個(gè)函數(shù)填充一個(gè)work結(jié)構(gòu)體。比如:

    b. 要執(zhí)行這個(gè)函數(shù)時(shí),把work提交給work queue就可以了:

    上述函數(shù)會(huì)把work提供給系統(tǒng)默認(rèn)的work queue:system_wq,它是一個(gè)隊(duì)列。

    c. 誰(shuí)來執(zhí)行work中的函數(shù)?

    不用我們管,schedule_work函數(shù)不僅僅是把work放入隊(duì)列,還會(huì)把kworker線程喚醒。此線程搶到時(shí)間運(yùn)行時(shí),它就會(huì)從隊(duì)列中取出work,執(zhí)行里面的函數(shù)。

    d. 誰(shuí)把work提交給work queue?

    在中斷場(chǎng)景中,可以在中斷上半部調(diào)用schedule_work函數(shù)。

    總結(jié)

    a. 很耗時(shí)的中斷處理,應(yīng)該放到線程里去

    b. 可以使用work、work queue

    c. 在中斷上半部調(diào)用schedule_work函數(shù),觸發(fā)work的處理

    d. 既然是在線程中運(yùn)行,那對(duì)應(yīng)的函數(shù)可以休眠。


    07

    新技術(shù):threaded irq

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

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

    好,內(nèi)核是為懶人服務(wù)的,再殺出一個(gè)函數(shù):

    你可以只提供thread_fn,系統(tǒng)會(huì)為這個(gè)函數(shù)創(chuàng)建一個(gè)內(nèi)核線程。發(fā)生中斷時(shí),內(nèi)核線程就會(huì)執(zhí)行這個(gè)函數(shù)。

    說你懶是開玩笑,內(nèi)核開發(fā)者也不會(huì)那么在乎懶人。

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

    新技術(shù)threaded irq,為每一個(gè)中斷都創(chuàng)建一個(gè)內(nèi)核線程;多個(gè)中斷的內(nèi)核線程可以分配到多個(gè)CPU上執(zhí)行,這提高了效率。

    ☆ END ☆

    掃碼或長(zhǎng)按關(guān)注

    回復(fù)「?加群?」進(jìn)入技術(shù)群聊

    創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)

    總結(jié)

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

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。