新聞中心
Linux內(nèi)核中斷函數(shù)的上半部分是指中斷處理的之一部分,也是最關(guān)鍵的部分。在這一部分中,內(nèi)核必須立即響應(yīng)中斷請求,并在處理完中斷請求后盡快恢復(fù)中斷,以確保系統(tǒng)的穩(wěn)定性和可靠性。因此,了解Linux內(nèi)核中斷函數(shù)的上半部分是非常重要的。

成都創(chuàng)新互聯(lián)是專業(yè)的衡東網(wǎng)站建設(shè)公司,衡東接單;提供成都網(wǎng)站設(shè)計、網(wǎng)站制作,網(wǎng)頁設(shè)計,網(wǎng)站設(shè)計,建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進行衡東網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團隊,希望更多企業(yè)前來合作!
中斷是計算機硬件和軟件之間的一種通信機制。當(dāng)硬件設(shè)備需要向計算機主機發(fā)出通知時,它會發(fā)送一個中斷請求信號(IRQ)。這個IRQ信號被傳送到計算機的中斷控制器中,中斷控制器將它轉(zhuǎn)換為一個中斷向量(中斷號)。然后,內(nèi)核的中斷處理程序?qū)⒈徽{(diào)用,對中斷進行響應(yīng)。
Linux內(nèi)核的中斷處理是分為兩個部分的:上半部分和下半部分。上半部分處理的是中斷的執(zhí)行過程,而下半部分處理的是中斷的清理過程。
在Linux內(nèi)核的中斷處理程序中,上半部分是最重要的部分。上半部分是處理中斷請求的之一部分,并且必須在最短時間內(nèi)執(zhí)行。它必須能夠盡可能快地完成中斷請求并快速釋放中斷,并將控制返回到應(yīng)用程序。
下面是Linux內(nèi)核中斷函數(shù)上半部分的主要任務(wù):
1.中斷處理程序的進入
當(dāng)中斷請求被激活時,控制權(quán)將從用戶空間轉(zhuǎn)移到內(nèi)核空間。然后,內(nèi)核開始執(zhí)行中斷處理程序。中斷處理程序負責(zé)檢查中斷請求,并響應(yīng)中斷。在進入中斷處理程序之前,內(nèi)核必須保存當(dāng)前的處理器狀態(tài),并確保處理器的狀態(tài)正確。
2.中斷請求的分配
當(dāng)中斷處理程序進入時,內(nèi)核必須分配一個中斷號。這樣,中斷控制器就可以將中斷請求轉(zhuǎn)發(fā)到正確的中斷處理程序。為了選擇正確的中斷號,內(nèi)核必須檢查中斷請求的來源,例如一個設(shè)備或驅(qū)動程序。
3.中斷的響應(yīng)
一旦確認了中斷請求來源并分配了中斷號,內(nèi)核就開始響應(yīng)中斷請求。這一步通常涉及到處理中斷請求數(shù)據(jù)、確定中斷請求時的事件和中斷的形式、并向設(shè)備驅(qū)動程序發(fā)送中斷請求(通常是下半部分的處理)。
4.中斷的處理
在執(zhí)行中斷請求時,內(nèi)核必須執(zhí)行相應(yīng)的中斷處理程序。這本質(zhì)上就是執(zhí)行打斷原來的操作,但是內(nèi)核必須確保進程能夠正確地恢復(fù)并繼續(xù)運行。
5.中斷的返回
當(dāng)內(nèi)核完成中斷處理時,控制權(quán)將返回到應(yīng)用程序。但是,內(nèi)核仍然需要確保狀態(tài)正確并清理中斷函數(shù)內(nèi)部的其他操作。這通常需要釋放鎖定資源,并確保系統(tǒng)狀態(tài)穩(wěn)定。
了解Linux內(nèi)核中斷函數(shù)的上半部分是非常重要的。上半部分是整個中斷處理程序的關(guān)鍵部分,負責(zé)響應(yīng)中斷并進行快速、穩(wěn)定的處理。在理解Linux內(nèi)核中斷函數(shù)的上半部分之后,下半部分的理解和處理將變得更加容易。
相關(guān)問題拓展閱讀:
- 關(guān)于linux注冊的中斷函數(shù)
- 如何在linux下開啟napi
關(guān)于linux注冊的中斷函數(shù)
我也不完全理解,但是比你知道的多點。
Linux中,分內(nèi)核態(tài)和用戶態(tài)。
你寫的所有的驅(qū)動,都是出于內(nèi)核態(tài)->可以直接使用內(nèi)核相關(guān)資源;
應(yīng)用層,都是用戶態(tài)->無法直接操作底層的東西 -> 想要操作,比如獲得權(quán)限,切換到內(nèi)核態(tài),然后才能操作。
你這里的需求,我的理解是:
對應(yīng)你這句
“在中斷服務(wù)程序中操作另一個外設(shè)”
不知道你的目的和打算用的手段是啥
一般的,ISR中,操作別的設(shè)備,常見的是:
設(shè)置對應(yīng)的(比如該硬件本身,或者別的設(shè)備B的)寄存器的對應(yīng)的位,以便通知其某種事情發(fā)送或狀態(tài)變化了。
然后設(shè)備B會:
要么是由于(被修改了寄存器而)發(fā)生了中斷,然后可以接著處理其所要做的事情;
要么是一直輪訓(xùn),檢測對應(yīng)的某種資源釋放變化,比如上面被改的寄存器的對應(yīng)的位,發(fā)現(xiàn)變化了,再去調(diào)用你的函數(shù),做對應(yīng)的處理。
注意:
中斷,不論是哪個設(shè)備的中斷,都不應(yīng)該占用(CPU)太長時間
-> 導(dǎo)致別的中斷或服務(wù)無法及時運行
僅供參考。
從內(nèi)核空間返回用戶空間時,kernel檢查是否有pending signal,如果有,執(zhí)行。
如何在linux下開啟napi
天重點對linux網(wǎng)絡(luò)
數(shù)據(jù)包
的處理做下分析,但是并不關(guān)系到上層協(xié)議,僅僅到鏈路層。
之前轉(zhuǎn)載過一篇文章,對NAPI做了比較詳盡的分析,本文結(jié)合Linux內(nèi)核源代碼,對當(dāng)前網(wǎng)絡(luò)數(shù)據(jù)包的處理進行梳理。根據(jù)NAPI的處理特性,對設(shè)備提出一定的要求
1、設(shè)備需要有足夠的緩沖區(qū),保存多個數(shù)據(jù)分組
2、可以禁用當(dāng)前設(shè)備中斷,然而不影響其他的操作。
當(dāng)前大部分的設(shè)備都支持NAPI,但是為了對之前的保持兼容,內(nèi)核還是對之前中斷方式提供了兼容。我們先看下NAPI具體的處理方式。我們都知道中斷分為中斷上半部和下半部,上半部完成的任務(wù)很是簡單,僅僅負責(zé)把數(shù)據(jù)保存下來;而下半部負責(zé)具體的處理。為了處理下半部,每個CPU有維護一個softnet_data結(jié)構(gòu)。我們不對此結(jié)構(gòu)做詳細介紹,僅僅描述和NAPI相關(guān)的部分。結(jié)構(gòu)中有一個poll_list字段,連接所有的輪詢設(shè)備。還 維護了兩個隊列input_pkt_queue和process_queue。這兩個用戶傳統(tǒng)不支持NAPI方式的處理。前者由中斷上半部的處理函數(shù)吧數(shù)據(jù)包入隊,在具體的處理時,使用后者做中轉(zhuǎn),相當(dāng)于前者負責(zé)接收,后者負責(zé)處理。最后是一個napi_struct的backlog,代表一個虛擬設(shè)備供輪詢使用。在支持NAPI的設(shè)備下,每個設(shè)備具備一個緩沖隊列,存放到來數(shù)據(jù)。每個設(shè)備對應(yīng)一個napi_struct結(jié)構(gòu),該結(jié)構(gòu)代表該設(shè)備存放在poll_list中被輪詢。而設(shè)備還需要提供一個poll函數(shù),在設(shè)備被輪詢到后,會調(diào)用poll函數(shù)對數(shù)據(jù)進行處理。基本邏輯就是這樣,下面看下具體流程。
中斷上半部:
非NAPI:
非NAPI對應(yīng)的上半部函數(shù)為netif_rx,位于Dev.,c中
int netif_rx(struct sk_buff *skb)
{
int ret;
/* if netpoll wants it, pretend we never saw it */
/*如果是net_poll想要的,則不作處理*/
if (netpoll_rx(skb))
return NET_RX_DROP;
/*檢查時間戳*/
net_timestamp_check(netdev_tstamp_prequeue, skb);
trace_netif_rx(skb);
#ifdef CONFIG_RPS
if (static_key_false(&rps_needed)) {
struct rps_dev_flow voidflow, *rflow = &voidflow;
int cpu;
/*禁用搶占*/
preempt_disable();
rcu_read_lock();
cpu = get_rps_cpu(skb->dev, skb, &rflow);
if (cpu last_qtail);
rcu_read_unlock();
preempt_enable();
} else
#endif
{
unsigned int
qtail;
ret = enqueue_to_backlog(skb, get_cpu(), &qtail);
put_cpu();
}
return ret;
}
中間RPS暫時不關(guān)心,這里直接調(diào)用enqueue_to_backlog放入CPU的全局隊列input_pkt_queue
static int enqueue_to_backlog(struct sk_buff *skb, int cpu,
unsigned int *qtail)
{
struct softnet_data *sd;
unsigned long flags;
/*獲取cpu相關(guān)的softnet_data變量*/
sd = &per_cpu(softnet_data, cpu);
/*關(guān)中斷*/
local_irq_save(flags);
rps_lock(sd);
/*如果input_pkt_queue的長度小于更大限制,則符合條件*/
if (skb_queue_len(&sd->input_pkt_queue) input_pkt_queue)) {
enqueue:
__skb_queue_tail(&sd->input_pkt_queue, skb);
input_queue_tail_incr_save(sd, qtail);
rps_unlock(sd);
local_irq_restore(flags);
return NET_RX_SUCCESS;
}
/* Schedule NAPI for backlog device
* We can use non atomic operation since we own the queue lock
*/
/*否則需要調(diào)度backlog 即虛擬設(shè)備,然后再入隊。napi_struct結(jié)構(gòu)中的state字段如果標記了NAPI_STATE_SCHED,則表明該設(shè)備已經(jīng)在調(diào)度,不需要再次調(diào)度*/
if (!__test_and_set_bit(NAPI_STATE_SCHED, &sd->backlog.state)) {
if (!rps_ipi_queued(sd))
____napi_schedule(sd, &sd->backlog);
}
goto enqueue;
}
/*到這里緩沖區(qū)已經(jīng)不足了,必須丟棄*/
sd->dropped++;
rps_unlock(sd);
local_irq_restore(flags);
atomic_long_inc(&skb->dev->rx_dropped);
kfree_skb(skb);
return NET_RX_DROP;
}
該函數(shù)邏輯也比較簡單,主要注意的是設(shè)備必須先添加調(diào)度然后才能接受數(shù)據(jù),添加調(diào)度調(diào)用了____napi_schedule函數(shù),該函數(shù)把設(shè)備對應(yīng)的napi_struct結(jié)構(gòu)插入到softnet_data的poll_list
鏈表
尾部,然后喚醒軟中斷,這樣在下次軟中斷得到處理時,中斷下半部就會得到處理。不妨看下源碼
static inline void ____napi_schedule(struct softnet_data *sd,
struct napi_struct *napi)
{
list_add_tail(&napi->poll_list, &sd->poll_list);
__raise_softirq_irqoff(NET_RX_SOFTIRQ);
}
NAPI方式
NAPI的方式相對于非NAPI要簡單許多,看下e100網(wǎng)卡的中斷處理函數(shù)e100_intr,核心部分
if (likely(napi_schedule_prep(&nic->napi))) {
e100_disable_irq(nic);//屏蔽當(dāng)前中斷
__napi_schedule(&nic->napi);//把設(shè)備加入到輪訓(xùn)隊列
}
if條件檢查當(dāng)前設(shè)備是否 可被調(diào)度,主要檢查兩個方面:1、是否已經(jīng)在調(diào)度 2、是否禁止了napi pending.如果符合條件,就關(guān)閉當(dāng)前設(shè)備的中斷,調(diào)用__napi_schedule函數(shù)把設(shè)備假如到輪訓(xùn)列表,從而開啟輪詢模式。
分析:結(jié)合上面兩種方式,還是可以發(fā)現(xiàn)兩種方式的異同。其中softnet_data作為主導(dǎo)結(jié)構(gòu),在NAPI的處理方式下,主要維護輪詢鏈表。NAPI設(shè)備均對應(yīng)一個napi_struct結(jié)構(gòu),添加到鏈表中;非NAPI沒有對應(yīng)的napi_struct結(jié)構(gòu),為了使用NAPI的處理流程,使用了softnet_data結(jié)構(gòu)中的back_log作為一個虛擬設(shè)備添加到輪詢鏈表。同時由于非NAPI設(shè)備沒有各自的接收隊列,所以利用了softnet_data結(jié)構(gòu)的input_pkt_queue作為全局的接收隊列。這樣就處理而言,可以和NAPI的設(shè)備進行兼容。但是還有一個重要區(qū)別,在NAPI的方式下,首次數(shù)據(jù)包的接收使用中斷的方式,而后續(xù)的數(shù)據(jù)包就會使用輪詢處理了;而非NAPI每次都是通過中斷通知。
下半部:
下半部的處理函數(shù),之前提到,網(wǎng)絡(luò)數(shù)據(jù)包的接發(fā)對應(yīng)兩個不同的軟中斷,接收軟中斷NET_RX_SOFTIRQ的處理函數(shù)對應(yīng)net_rx_action
static void net_rx_action(struct softirq_action *h)
{
struct softnet_data *sd = &__get_cpu_var(softnet_data);
unsigned long time_limit = jiffies + 2;
int budget = netdev_budget;
void *have;
local_irq_disable();
/*遍歷輪詢表*/
while (!list_empty(&sd->poll_list)) {
struct napi_struct *n;
int work, weight;
/* If softirq window is exhuasted then punt.
* Allow this to run for 2 jiffies since which will allow
* an average latency of 1.5/HZ.
*/
/*如果開支用完了或者時間用完了*/
if (unlikely(budget poll()
* calls can remove this head entry from the list.
*/
/*獲取鏈表中首個設(shè)備*/
n = list_first_entry(&sd->poll_list, struct napi_struct, poll_list);
have = netpoll_poll_lock(n);
weight = n->weight;
/* This NAPI_STATE_SCHED test is for avoiding a race
* with netpoll’s poll_napi(). Only the entity which
* obtains the lock and sees NAPI_STATE_SCHED set will
* actually make the ->poll() call. Therefore we avoid
* accidentally calling ->poll() when NAPI is not scheduled.
*/
work = 0;
/*如果被設(shè)備已經(jīng)被調(diào)度,則調(diào)用其處理函數(shù)poll函數(shù)*/
if (test_bit(NAPI_STATE_SCHED, &n->state)) {
work = n->poll(n, weight);//后面weight指定了一個額度
trace_napi_poll(n);
}
WARN_ON_ONCE(work > weight);
/*總額度遞減*/
budget -= work;
local_irq_disable();
/* Drivers must not modify the NAPI state if they
* consume the entire weight. In such cases this code
* still “owns” the NAPI instance and therefore can
* move the instance around on the list at-will.
*/
/*如果work=weight的話。任務(wù)就完成了,把設(shè)備從輪詢鏈表刪除*/
if (unlikely(work == weight)) {
if (unlikely(napi_disable_pending(n))) {
local_irq_enable();
napi_complete(n);
local_irq_disable();
} else {
if (n->gro_list) {
/* flush too old packets
* If HZ = 1000);
local_irq_disable();
}
/*每次處理完就把設(shè)備移動到列表尾部*/
list_move_tail(&n->poll_list, &sd->poll_list);
}
}
netpoll_poll_unlock(have);
}
out:
net_rps_action_and_irq_enable(sd);
#ifdef CONFIG_NET_DMA
/*
* There may not be any more sk_buffs coming right now, so push
* any pending DMA copies to hardware
*/
dma_issue_pending_all();
#endif
return;
softnet_break:
sd->time_squeeze++;
__raise_softirq_irqoff(NET_RX_SOFTIRQ);
goto out;
}
這里有處理方式比較直觀,直接遍歷poll_list鏈表,處理之前設(shè)置了兩個限制:budget和time_limit。前者限制本次處理數(shù)據(jù)包的總量,后者限制本次處理總時間。只有二者均有剩余的情況下,才會繼續(xù)處理。處理期間同樣是開中斷的,每次總是從鏈表表頭取設(shè)備進行處理,如果設(shè)備被調(diào)度,其實就是檢查NAPI_STATE_SCHED位,則調(diào)用 napi_struct的poll函數(shù),處理結(jié)束如果沒有處理完,則把設(shè)備移動到鏈表尾部,否則從鏈表刪除。NAPI設(shè)備對應(yīng)的poll函數(shù)會同樣會調(diào)用__netif_receive_skb函數(shù)上傳協(xié)議棧,這里就不做分析了,感興趣可以參考e100的poll函數(shù)e100_poll。
而非NAPI對應(yīng)poll函數(shù)為process_backlog。
static int process_backlog(struct napi_struct *napi, int quota)
{
int work = 0;
struct softnet_data *sd = container_of(napi, struct softnet_data, backlog);
#ifdef CONFIG_RPS
/* Check if we have pending ipi, its better to send them now,
* not waiting net_rx_action() end.
*/
if (sd->rps_ipi_list) {
local_irq_disable();
net_rps_action_and_irq_enable(sd);
}
#endif
napi->weight = weight_p;
local_irq_disable();
while (work process_queue))) {
local_irq_enable();
/*進入?yún)f(xié)議棧*/
__netif_receive_skb(skb);
local_irq_disable();
input_queue_head_incr(sd);
if (++work >= quota) {
local_irq_enable();
return work;
}
}
rps_lock(sd);
qlen = skb_queue_len(&sd->input_pkt_queue);
if (qlen)
skb_queue_splice_tail_init(&sd->input_pkt_queue,
&sd->process_queue);
if (qlen poll_list);
napi->state = 0;
quota = work + qlen;
}
rps_unlock(sd);
}
local_irq_enable();
return work;
}
函數(shù)還是比較簡單的,需要注意的每次處理都攜帶一個配額,即本次只能處理quota個數(shù)據(jù)包,如果超額了,即使沒處理完也要返回,這是為了保證處理器的公平使用。處理在一個while循環(huán)中完成,循環(huán)條件正是work = quota時,就要返回。當(dāng)work還有剩余額度,但是process_queue中數(shù)據(jù)處理完了,就需要檢查input_pkt_queue,因為在具體處理期間是開中斷的,那么期間就有可能有新的數(shù)據(jù)包到來,如果input_pkt_queue不為空,則調(diào)用skb_queue_splice_tail_init函數(shù)把數(shù)據(jù)包遷移到process_queue。如果剩余額度足夠處理完這些數(shù)據(jù)包,那么就把虛擬設(shè)備移除輪詢隊列。這里有些疑惑就是最后為何要增加額度,剩下的額度已經(jīng)足夠處理這些數(shù)據(jù)了呀?根據(jù)此流程不難發(fā)現(xiàn),其實執(zhí)行的是在兩個隊列之間移動數(shù)據(jù)包,然后再做處理。
關(guān)于linux中斷函數(shù)上半部分的介紹到此就結(jié)束了,不知道你從中找到你需要的信息了嗎 ?如果你還想了解更多這方面的信息,記得收藏關(guān)注本站。
成都服務(wù)器托管選創(chuàng)新互聯(lián),先上架開通再付費。
創(chuàng)新互聯(lián)(www.cdcxhl.com)專業(yè)-網(wǎng)站建設(shè),軟件開發(fā)老牌服務(wù)商!微信小程序開發(fā),APP開發(fā),網(wǎng)站制作,網(wǎng)站營銷推廣服務(wù)眾多企業(yè)。電話:028-86922220
當(dāng)前題目:Linux內(nèi)核中斷函數(shù)的上半部分詳解 (linux中斷函數(shù)上半部分)
當(dāng)前URL:http://fisionsoft.com.cn/article/cogdgej.html


咨詢
建站咨詢
