最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

萬(wàn)字講解Linux內(nèi)核網(wǎng)絡(luò)收包角度——淺入中斷(1)

2022-10-21 14:39 作者:補(bǔ)給站Linux內(nèi)核  | 我要投稿

一、先拋開(kāi)網(wǎng)絡(luò)收包引起的一系列相關(guān)知識(shí),看ARM下中斷相關(guān)的概念

幾個(gè)概念:

外設(shè) :

中斷源,當(dāng)設(shè)備需要請(qǐng)求某種服務(wù)時(shí),會(huì)發(fā)起一個(gè)硬件中斷信號(hào),將信號(hào)傳遞到中斷控制器輸入引腳上。

中斷控制器:

中斷控制器負(fù)責(zé)收集所有中斷源發(fā)起的中斷,轉(zhuǎn)換成CPU可識(shí)別的vector,相當(dāng)于一個(gè)代理,如ARM公司提供的GIC中斷控制器,有四個(gè)版本GIC v1~v4。外部設(shè)備產(chǎn)生的中斷事件不會(huì)直接通過(guò)NTR總線進(jìn)入CPU,而是先發(fā)給中斷控制器,中斷控制器再轉(zhuǎn)發(fā)給CPU。中斷控制器可以:管理、控制可屏蔽中斷、對(duì)可屏蔽中斷進(jìn)行優(yōu)先權(quán)判定。假設(shè)沒(méi)有中斷控制器,CPU為每一個(gè)外設(shè)都準(zhǔn)備一個(gè)引腳,完全是不現(xiàn)實(shí)的,一方面引腳不夠用,還會(huì)增大CPU體積,最重要的是需要維護(hù)一個(gè)中斷等待隊(duì)列,優(yōu)先級(jí)的判斷由CPU來(lái)做,極大地降低了CPU的效率,這也是中斷控制器存在的意義所在。

異常向量表:

異常指CPU的某些異常狀態(tài)或者一些系統(tǒng)事件(可能來(lái)自外部,也可能來(lái)自內(nèi)部),這些狀態(tài)或者事件可以導(dǎo)致cpu執(zhí)行一些預(yù)先設(shè)定的動(dòng)作,CPU每執(zhí)行完一條指令都會(huì)檢查有無(wú)異常發(fā)生。 ARM中異常向量如下表所示:



__vectors_start是異常向量的基地址,如下所示:

HW interrupt ID:

對(duì)于中斷控制器而言,收集了多個(gè)外設(shè)的中斷設(shè)備的請(qǐng)求,并向上傳遞,因此中斷控制器需要對(duì)外設(shè)中斷進(jìn)行編碼,中斷控制器用HW interrupt ID來(lái)標(biāo)識(shí)外設(shè)的中斷。但是在中斷控制器級(jí)聯(lián)的情況下,僅僅用HW interrupt ID已經(jīng)不能唯一標(biāo)識(shí)一個(gè)外設(shè)中斷 ,如下所示:



此時(shí)還需要知道該HW interrupt ID所屬的中斷控制器,因此引入了irq domain的概念,一個(gè)中斷控制器就是一個(gè)irq domain 。索引每個(gè)中斷控制器對(duì)應(yīng)自己的中斷號(hào),硬件中斷號(hào)在不同控制器上是可以重復(fù)編碼的。

IRQ number:

CPU需要為每一個(gè)外設(shè)中斷編號(hào),我們稱之IRQ Number。這個(gè)IRQ number是一個(gè)虛擬的interrupt ID,和硬件無(wú)關(guān),僅僅是被CPU用來(lái)標(biāo)識(shí)一個(gè)外設(shè)中斷。(中斷控制器級(jí)聯(lián)情況下,硬件中斷號(hào)無(wú)法做到ID的唯一性)

關(guān)于IRQ_Number與HWIRQ,可通過(guò)cat /proc/interrupt查看:



中斷上半部、下半部:

設(shè)備的中斷會(huì)打斷內(nèi)核中進(jìn)程的正常調(diào)度和運(yùn)行 ,在中斷到來(lái)時(shí),要完成的工作往往比較復(fù)雜,可能要大量的耗時(shí)處理,為了在中斷執(zhí)行事件盡可能短和中斷處理需完成大量工作之間找一個(gè)平衡點(diǎn) ,Linux將中斷處理程序分解為兩個(gè)半部:top half 、bottom half,也就是中斷上半部和中斷下半部,上半部往往完成盡可能少的比較緊急的工作,它往往只是簡(jiǎn)單地讀取寄存器中的中斷狀態(tài)并清除中斷標(biāo)志后就進(jìn)行"登記中斷"的工作,后續(xù)工作交給下半部完成。

軟中斷:

中斷的下半部實(shí)現(xiàn)方式有3種,分別是軟中斷、tasklet、工作隊(duì)列,軟中斷是下半部的實(shí)現(xiàn)方式之一,就是在硬件中斷(也叫中斷頂半部分)執(zhí)行完畢后,通過(guò)wakeup_softirqd()的方式喚醒一個(gè)softirq隊(duì)列,然后中斷程序返回,softirq隊(duì)列也在適當(dāng)?shù)臅r(shí)候開(kāi)始執(zhí)行。


【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【891587639】整理了一些個(gè)人覺(jué)得比較好的學(xué)習(xí)書(shū)籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。。。ê曨l教程、電子書(shū)、實(shí)戰(zhàn)項(xiàng)目及代碼)??


中斷描述符表:

在Linux Kernel中,對(duì)于每一個(gè)外設(shè)的IRQ都有統(tǒng)一的描述方式,在Linux Kernel中用struct irq_desc來(lái)表示,稱為中斷描述符,保存了中斷相關(guān)的信息,如IRQ_Number、硬件中斷號(hào)、中斷服務(wù)例程等:

而中斷描述符表有兩種組織方式,第一種是數(shù)組的方式:

NR_IRQS決定了該硬件平臺(tái)的IRQ最大數(shù)目,但用數(shù)組的方式,一旦系統(tǒng)定義了很大的NR_IRQS,實(shí)際如果用了其中了少部分,那會(huì)造成很大的內(nèi)存浪費(fèi)。第二種是radix tree的方式,使用HW interruput作為索引,每一個(gè)中斷描述符動(dòng)態(tài)分配。在內(nèi)核種查看CONFIG_SPACE_IRQ選項(xiàng)是否開(kāi)啟,若開(kāi)啟說(shuō)明采用的是radix tree的方式:



中斷服務(wù)例程:

上面的中斷描述表中也介紹到,中斷描述表struct irq_desc中存放著中斷相關(guān)信息,一個(gè)中斷所需要的資源都集中在這個(gè)結(jié)構(gòu)體中描述,其中最重要的之一就是中斷服務(wù)例程,struct irq_desc中斷描述符的struct irq_action組成一個(gè)鏈表,每個(gè)irq_action都有回調(diào)函數(shù):handler執(zhí)行中斷處理,如下圖所示,組織成鏈表是因?yàn)橐粋€(gè)中斷線上可能會(huì)掛載好幾個(gè)設(shè)備,這幾個(gè)設(shè)備會(huì)共用這個(gè)中斷,具體是哪個(gè)設(shè)備需要判斷。



注冊(cè)中斷:

從上面的中斷服務(wù)例程中可以到,中斷的相應(yīng)處理是通過(guò)中斷描述符為橋梁進(jìn)行調(diào)用一系列中斷處理函數(shù)的,Linux內(nèi)核API:


//irq是中斷編號(hào)、myhandler是中斷處理函數(shù)、flages是中斷類型,name是設(shè)備名稱、dev指設(shè)備 request_irq(unsigned ?int ?irq, ?irq_handler_t handler, unsigned long flags, const char *name, void *dev

該函數(shù)實(shí)現(xiàn)動(dòng)態(tài)地申請(qǐng)注冊(cè)一個(gè)中斷,根據(jù)傳入的irq號(hào)獲得irq_desc文件描述符,然后動(dòng)態(tài)地創(chuàng)建一個(gè)irq_action,根據(jù)傳入的handler回調(diào)函數(shù)初始化irq_action,最后把irq_action鏈入鏈表,完成中斷的動(dòng)態(tài)申請(qǐng)及注冊(cè)。

基礎(chǔ)案例如下:

測(cè)試:



tips: 測(cè)試后盡快卸載掉內(nèi)核模塊,否則日志累加很快占用大量磁盤(pán)空間

二、在介紹Linux內(nèi)核網(wǎng)卡收包時(shí)的中斷流程前,再把網(wǎng)卡注冊(cè)硬中斷的過(guò)程分析一下

1、通過(guò)ethtool工具查看當(dāng)前網(wǎng)卡的網(wǎng)卡驅(qū)動(dòng)信息:



2、分析e1000網(wǎng)卡驅(qū)動(dòng)注冊(cè)中斷過(guò)程:

其模塊初始化函數(shù):

e1000_driver這個(gè)結(jié)構(gòu)體是一個(gè)關(guān)鍵,它的賦值如下:

其中很主要的一個(gè)方法就是.probe方法,也就是e1000_probe():

這是e1000主要的初始化函數(shù),其注冊(cè)了netdev的netdev_ops,用的是e1000_netdev_ops這個(gè)結(jié)構(gòu)體:

e1000_open函數(shù)就是中斷注冊(cè)開(kāi)始的地 方!** 網(wǎng)卡設(shè)備的啟動(dòng)與關(guān)閉網(wǎng)卡設(shè)備啟動(dòng)時(shí)首先調(diào)用函數(shù)e1000_open()**

e1000在這里注冊(cè)了中斷,中斷處理函數(shù)handler是e1000_intr()

至此一個(gè)網(wǎng)卡的重點(diǎn)處理函數(shù)注冊(cè)完畢。

三、網(wǎng)卡收包時(shí)硬中斷處理過(guò)程

**1、**數(shù)據(jù)包達(dá)到網(wǎng)卡,存入網(wǎng)卡硬件的緩沖區(qū),然后將數(shù)據(jù)包DMA到內(nèi)存中的緩沖區(qū),這個(gè)過(guò)程不需要CPU參與,只需要DMA這個(gè)硬件設(shè)備,配合網(wǎng)卡這個(gè)設(shè)備即可,這個(gè)過(guò)程的前提是網(wǎng)卡驅(qū)動(dòng)需要在內(nèi)存中申請(qǐng)一個(gè)struct sk_buffer的緩沖區(qū),然后把這個(gè)sk_buffer的地址告知網(wǎng)卡,這樣DMA時(shí)才能把數(shù)據(jù)拷貝到對(duì)應(yīng)的位置上。



2、**在1、中整個(gè)過(guò)程由硬件完成,完成后需要網(wǎng)卡來(lái)通知內(nèi)核,讓內(nèi)核處理數(shù)據(jù)包,網(wǎng)卡向 CPU 發(fā)起中斷信號(hào),CPU 打斷當(dāng)前的程序:

在一、 中介紹了異常向量表,異常向量表 vectors 中設(shè)置了各種異常的入口,網(wǎng)絡(luò)收包行為引起的是異步中斷,CPU跳轉(zhuǎn)到異步中斷異常向量處,執(zhí)行對(duì)應(yīng)的異常處理執(zhí)行:


使用宏vector_stub表示這個(gè)vector_irq:

3、**上面2、中提到的irq_user與irq_svc具體實(shí)現(xiàn)大致相同,主要是根據(jù)被中斷時(shí)CPU所處的狀態(tài)進(jìn)行選擇,以irq_user為例:

函數(shù)主要執(zhí)行:保存現(xiàn)場(chǎng)、調(diào)用irq_handler、恢復(fù)現(xiàn)場(chǎng),核心是保存現(xiàn)場(chǎng)后,跳入中斷處理irq_handler,在ARM 32上irq_handler如下:

CONFIG_MULTI_IRQ_HANDLER宏表示"允許每臺(tái)機(jī)器在運(yùn)行時(shí)指定它自己的IRQ處理程序",當(dāng)前Linux內(nèi)核默認(rèn)是不開(kāi)啟的.所以走else的arch_irq_handler_default邏輯arch_irq_handler_default 宏 調(diào)用了asm_do_IRQ.

在ARM 64下:handle_arch_irq如下,

采用ARM 64進(jìn)行分析,irq_handler將調(diào)用gic_handle_irq:

該函數(shù)首先讀取處理器接口中的中斷確認(rèn)寄存器得到硬件中斷號(hào) ,當(dāng)硬件中斷號(hào)大于15且小于1020或者硬件中斷號(hào)大于或等于8192時(shí),中斷來(lái)自外設(shè),調(diào)用函數(shù)handle_domain_irq():

該函數(shù):

  1. 調(diào)用irq_enter()進(jìn)入中斷上下文

  2. 調(diào)用函數(shù)irq_find_mapping根據(jù)硬件中斷號(hào)查找Linux中斷號(hào)(IRQ_number)

  3. 調(diào)用generic_handle_irq()函數(shù)處理中斷,該函數(shù)是底層架構(gòu)無(wú)關(guān)層的入口,也就是中斷通用層,在該層通過(guò)Linux中斷號(hào)找到了對(duì)應(yīng)的中斷描述符,并通過(guò)generic_handle_irq_desc()函數(shù)調(diào)用中斷描述符對(duì)應(yīng)的handle_irq()函數(shù)

  4. irq_exit()函數(shù)退出中斷上下文

**4、**詳細(xì)看generic_handle_irq:

generic_handle_irq_desc():

關(guān)于desc->handle_irq來(lái)歷,由gic_irq_domain_map根據(jù)hwirq覺(jué)得,硬件中斷號(hào)小于32的指向handle_percpu_devid_irq,其他情況指向handle_fasteoi_irq,網(wǎng)卡的硬件中斷號(hào)是大于32的,所以指向handle_fasteoi_irq函數(shù):

handle_fasteoi_irq->handle_irq_event->handle_irq_event_percpu:

在for_each_action_of_desc中:遍歷中斷描述符中的action鏈表,依次執(zhí)行每個(gè)action元素中的primary handler回調(diào)函數(shù)action->handler。

在二、 中分析了網(wǎng)卡e1000網(wǎng)卡驅(qū)動(dòng)注冊(cè)的中斷處理函數(shù)是:e1000_intr

**5、**在e1000_intr中看一下關(guān)鍵的部分:

__napi_schedule(&adapter->napi)函數(shù)激活NAPI,關(guān)于NAPI在以后的文字中進(jìn)行總結(jié),這里先大概了解:【隨著網(wǎng)絡(luò)帶寬的發(fā)展,網(wǎng)速越來(lái)越快,之前的中斷收包模式已經(jīng)無(wú)法適應(yīng)目前千兆,萬(wàn)兆的帶寬,每次收包都發(fā)生硬中斷通知CPU,CPU一直陷入硬中斷而沒(méi)有時(shí)間來(lái)處理別的事情了。為了解決這個(gè)問(wèn)題,內(nèi)核在2.6中引入了NAPI機(jī)制。NAPI就是混合中斷和輪詢的方式來(lái)收包,當(dāng)有中斷來(lái)了,驅(qū)動(dòng)關(guān)閉中斷,通知內(nèi)核收包,內(nèi)核軟中斷輪詢當(dāng)前網(wǎng)卡,在規(guī)定時(shí)間盡可能多的收包。時(shí)間用盡或者沒(méi)有數(shù)據(jù)可收,內(nèi)核再次開(kāi)啟中斷,準(zhǔn)備下一次收包。

具體處理過(guò)程調(diào)用 ____napi_schedule(this_cpu_ptr(&softnet_data), n);

NAPI的激活如上函數(shù):

1、內(nèi)核網(wǎng)絡(luò)系統(tǒng)在初始化時(shí)每個(gè)CPU都會(huì)有一個(gè)結(jié)構(gòu)體,它會(huì)把隊(duì)列對(duì)應(yīng)的信息插入到結(jié)構(gòu)體的鏈表里,換句話說(shuō),每個(gè)網(wǎng)卡隊(duì)列在接收數(shù)據(jù)的時(shí)候,需要把自己的隊(duì)列信息告訴對(duì)應(yīng)的CPU,將這兩個(gè)信息綁定起來(lái),保證某個(gè)CPU處理某個(gè)隊(duì)列。

2、要與觸發(fā)硬中斷一樣,需要觸發(fā)軟中斷,其實(shí)就是修改 pending 的某個(gè)標(biāo)志位,然后內(nèi)核中有一個(gè)線程不斷輪詢這組標(biāo)志位,看哪個(gè)是 1 了,就去軟中斷向量表里,尋找這個(gè)標(biāo)志位對(duì)應(yīng)的處理程序,然后執(zhí)行它。

可以看到上面網(wǎng)卡硬中斷的處理函數(shù)做的事情非常簡(jiǎn)單:將網(wǎng)卡設(shè)備 dev 放入 poll_lis輪詢列表 里,然后立刻發(fā)起了一次軟中斷

**6、**接下來(lái)就是執(zhí)行irq_exit函數(shù),這里是軟中斷和硬中斷銜接的一個(gè)地方:

在irq_exit()的第一步就是一個(gè)local_irq_disable(),也就是說(shuō)禁止了中斷,不再響應(yīng)中斷。因?yàn)橄旅嬉幚硭袠?biāo)記為要處理的軟中斷,關(guān)中斷是因?yàn)楹竺嬉宄@些軟中斷,將CPU軟中斷的位圖中置位的位清零,這需要關(guān)中斷,防止其它進(jìn)程對(duì)位圖的修改造成干擾。

然后preempt_count_sub(HARDIRQ_OFFSET),硬中斷的計(jì)數(shù)減1,表示當(dāng)前的硬中斷到這里就結(jié)束了。

假設(shè)當(dāng)前中斷結(jié)束后沒(méi)有其它中斷了,也就是不在中斷上下文了,且當(dāng)前CPU有等待處理的軟中斷,即local_softirq_pending()也為真。那么執(zhí)行invoke_softirq() ,邏輯就是:首先如果ksoftirqd正在被執(zhí)行,那么不處理被pending的軟中斷,交給ksoftirqd線程來(lái)處理,這里直接退出。如果ksoftirqd沒(méi)有正在運(yùn)行,那么判斷force_irqthreads,也就是判斷是否配置了CONFIG_IRQ_FORCED_THREADING,是否要求強(qiáng)制將軟中斷處理都交給ksoftirqd線程。因?yàn)檫@里明顯要在中斷處理退出的最后階段處理軟中斷,但是也可以讓ksoftirqd來(lái)后續(xù)處理。如果設(shè)置了force_irqthreads,則不再執(zhí)行__do_softirq(),轉(zhuǎn)而執(zhí)行wakeup_softirqd()來(lái)喚醒ksoftirqd線程,將其加入可運(yùn)行隊(duì)列,然后退出。如果沒(méi)有設(shè)置force_irqthreads,那么就執(zhí)行__do_softirq()

這里就完成網(wǎng)卡硬中斷與軟中斷的銜接,交給ksoftirqd線程或其他進(jìn)行軟中斷處理,關(guān)于軟中斷以及NAPI相關(guān)的知識(shí),在下幾次文章中分析。





萬(wàn)字講解Linux內(nèi)核網(wǎng)絡(luò)收包角度——淺入中斷(1)的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
霍邱县| 宁陵县| 唐河县| 延寿县| 黑山县| 革吉县| 庐江县| 云林县| 平江县| 二连浩特市| 周口市| 罗山县| 雷山县| 富蕴县| 阿拉善左旗| 嘉兴市| 兴仁县| 安新县| 青岛市| 韶山市| 福州市| 太原市| 绩溪县| 柯坪县| 孟村| 鄂托克旗| 六安市| 马尔康县| 渭源县| 城固县| 彭山县| 瓮安县| 太康县| 景谷| 宜宾县| 杭锦后旗| 临漳县| 鄂温| 临汾市| 积石山| 浏阳市|