国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
中斷下半部分析_softirq


------------------------------------------
一、概念
首先我們要知道為什么中斷需要下半部 。我們可以想象一下,如果沒有下半部的概念,一個(gè)網(wǎng)卡中斷過來了以后會(huì)是什么樣的情況。首先,我們會(huì)從網(wǎng)卡硬件buffer中把網(wǎng)卡收到的packet拷貝到系統(tǒng)內(nèi)存中,然后對(duì)這個(gè)packet進(jìn)行TCP/IP協(xié)議棧的處理。我們知道TCP/IP協(xié)議棧是一個(gè)比較復(fù)雜的軟件模塊,里面對(duì)packet的處理會(huì)經(jīng)過非常多的步驟,首先是鏈路層,然后是IP層(這里又包括分片,奇偶校驗(yàn)之類的),然后是TCP層(TCP層的實(shí)現(xiàn)相當(dāng)復(fù)雜,會(huì)花費(fèi)比較長的時(shí)間對(duì)packet進(jìn)行一些狀態(tài)或者內(nèi)容的分析處理),最后通過socket把packet傳入用戶空間。在傳入用戶空間之間的這些動(dòng)作,都必須在中斷處理中完成,因?yàn)檫@些操作都是在kernel中的,并且這些操作會(huì)花費(fèi)比較長的時(shí)間。在這段時(shí)間里,cpu由于進(jìn)入了中斷門,會(huì)自動(dòng)關(guān)中斷,也就是說cpu不會(huì)去響應(yīng)在這段時(shí)間里網(wǎng)卡另外發(fā)過來的中斷,這樣的話很有可能網(wǎng)卡硬件buffer會(huì)由于網(wǎng)卡自身的緩存不足而導(dǎo)致丟包    。所以linux為了解決這樣的問題,把copy packet這樣比較緊急的動(dòng)作放在了上半部去處理(上半部默認(rèn)情況下是在關(guān)中斷中完成的),把協(xié)議棧這些不是特別緊急的任務(wù)放到了下半部去處理(下半部是在開中斷中進(jìn)行的,有就是說,處理下半部的過程中,允許cpu被其他中斷打斷)。

二、軟件構(gòu)架和實(shí)現(xiàn)
1.       一些基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)
文件softirq.c
/*PER-CPU變量,每個(gè)cpu對(duì)應(yīng)一個(gè),描述當(dāng)前cpu中關(guān)于softirq的一些狀態(tài),比如是否有softirq掛起需要執(zhí)行等等*/
irq_cpustat_t irq_stat[NR_CPUS] ____cacheline_aligned;

typedef struct {
    unsigned int __softirq_pending; /*32位,對(duì)應(yīng)Linux中32種softirq是否被上半部觸發(fā)了(為1表示被觸發(fā),為0表示未被觸發(fā))*/
    unsigned long idle_timestamp;
    unsigned int __nmi_count;   /* arch dependent */
    unsigned int apic_timer_irqs;   /* arch dependent */
} ____cacheline_aligned irq_cpustat_t;

/*表示softirq最多有32種類型,實(shí)際上Linux只用了6種,見文件interrupt.h*/
static struct softirq_action softirq_vec[32] __cacheline_aligned_in_smp;

/* PLEASE, avoid to allocate new softirqs, if you need not _really_ high
   frequency threaded job scheduling. For almost all the purposes
   tasklets are more than enough. F.e. all serial device BHs et
   al. should be converted to tasklets, not to softirqs.
*/

enum
{
    HI_SOFTIRQ=0,   /*用于高優(yōu)先級(jí)的tasklet*/
    TIMER_SOFTIRQ, /*用于定時(shí)器的下半部*/
    NET_TX_SOFTIRQ,/*用于網(wǎng)絡(luò)層發(fā)包*/
    NET_RX_SOFTIRQ, /*用于網(wǎng)絡(luò)層收包*/
    SCSI_SOFTIRQ,   /*用于SCSI設(shè)備*/
    TASKLET_SOFTIRQ /*用于低優(yōu)先級(jí)的tasklet*/
};

struct softirq_action
{
    void    (*action)(struct softirq_action *);  /*softirq的回調(diào)函數(shù)*/
    void    *data; /*傳入action的參數(shù)*/
};

Struct softirq_action是每個(gè)softirq的配置結(jié)構(gòu),一般在系統(tǒng)啟動(dòng)的時(shí)候,6個(gè)不同的softirq,會(huì)通過函數(shù)open_softirq()來注冊自己的softirq_action,實(shí)現(xiàn)很簡單:
void open_softirq(int nr, void (*action)(struct softirq_action*), void *data)
{
    softirq_vec[nr].data = data;
    softirq_vec[nr].action = action;
}
關(guān)鍵是傳入的函數(shù)指針,具體指明了該softirq要實(shí)現(xiàn)的功能或要做的動(dòng)作。
這里分開看下這六個(gè)注冊點(diǎn):
Net/core/dev.c中的net_dev_init()里面注冊了網(wǎng)絡(luò)層需要用到的收包和發(fā)包的兩個(gè)softirq:
    open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL);
    open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL);
對(duì)應(yīng)的函數(shù)指針為net_tx_action和net_rx_action,具體功能就不在本文的范圍之內(nèi)的。

Driver/scsi/scsi.c中init_scsi()注冊了SCSI_SOFTIRQ
open_softirq(SCSI_SOFTIRQ, scsi_softirq, NULL);

start_kernel() àsiftirq_init() 中注冊了兩種tasklet,一種是高優(yōu)先級(jí)的tasklet,一直是低優(yōu)先級(jí)的tasklet:
       open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
       open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);

start_kernel() àinit_timers() 中注冊了TIMER_SOFTIRQ
       open_softirq(TIMER_SOFTIRQ, run_timer_softirq, NULL);

2.       softirq運(yùn)行時(shí)機(jī):
系統(tǒng)在運(yùn)行過程中,會(huì)在合適的地方使用函數(shù)local_softirq_pending()檢查系統(tǒng)是否有softirq需要處理;需要時(shí)會(huì)調(diào)用函數(shù)do_softirq()進(jìn)行處理。這些檢查點(diǎn)主要包括以下幾個(gè)地方:
(1)    中斷過程退出函數(shù)irq_exit();
(2)    內(nèi)核線程ksoftirqd;
(3)    內(nèi)核網(wǎng)絡(luò)子系統(tǒng)中顯示調(diào)用;
(4)    函數(shù)local_bh_enable().

先前我們分析irq_cpustat_t結(jié)構(gòu)的時(shí)候,看到__softirq_pending字段。這是一個(gè)32位無符號(hào)的變量,對(duì)應(yīng)Linux中32種softirq是否被上半部觸發(fā)了(為1表示被觸發(fā),為0表示未被觸發(fā))。那么在softirq運(yùn)行之前肯定就有地方設(shè)置了這個(gè)變量的各個(gè)位,才會(huì)觸發(fā)到softirq運(yùn)行。這個(gè)觸發(fā)動(dòng)作一般是在上半部中進(jìn)行的,即上半部通過系統(tǒng),還有下半部需要運(yùn)行。觸發(fā)函數(shù)如下:
#define __raise_softirq_irqoff(nr) do { or_softirq_pending(1UL
#define or_softirq_pending(x)  (local_softirq_pending() |= (x))
#define local_softirq_pending() \
       __IRQ_STAT(smp_processor_id(), __softirq_pending)
#define __IRQ_STAT(cpu, member)     (irq_stat[cpu].member)
由此可以看出,  __raise_softirq_irqoff(nr)實(shí)際上是把當(dāng)前cpu的per-cpu變量irq_stat的__softirq_pending 的從右往左數(shù)的第nr位置1,這樣系統(tǒng)就知道某個(gè)softirq需要在某個(gè)時(shí)刻運(yùn)行了。

當(dāng)然,__raise_sofritq_irqoff()被很多地方封裝過,不同子系統(tǒng)用自己封裝的函數(shù),比如網(wǎng)絡(luò)子系統(tǒng)就用netif_rx_reschedule和net_rx_action來激活softirq。

3.       softirq的執(zhí)行分析:
我們先提到了softirq在四個(gè)地方有可能運(yùn)行,最常見的就是中斷過程退出函數(shù)irq_exit(),我們下面分析之,其他的觸發(fā)點(diǎn)請(qǐng)大家對(duì)照代碼自行分析:

void irq_exit(void)
{
       account_system_vtime(current);
       sub_preempt_count(IRQ_EXIT_OFFSET);
       /*如果在上半部中設(shè)置了per-cpu變量irq_stat的__softirq_pending字段,則運(yùn)行下半部的處理函數(shù), 從這里也可以看出,上半部和下半部一定是運(yùn)行在同一個(gè)cpu上,因?yàn)樯习氩恐惺窃O(shè)置了per-cpu變量irq_stat本地cpu副本中的__softirq_pending中的位,而下半部也只是判斷本地cpu的__sofrirq_pending中的位。這樣有效的利用了cpu cache的特性*/
       if (!in_interrupt() && local_softirq_pending())
              invoke_softirq();
       preempt_enable_no_resched();
}

Invoke_sofirq()àdo_softirq()

asmlinkage void do_softirq(void)
{
       __u32 pending;
       unsigned long flags;
        /*本地cpu中,softirq不能在中斷環(huán)境中運(yùn)行,這個(gè)中斷環(huán)境包括了上半部和下半部,所以這里保證了同一個(gè)cpu上,下半部是不會(huì)被重入的,但不能保證其他cpu上的同時(shí)運(yùn)行同一個(gè)softirq處理。所以編程人員必須讓自己編寫的softirq處理函數(shù)可重入,以防SMP系統(tǒng)中同時(shí)運(yùn)行這些softirq導(dǎo)致數(shù)據(jù)出現(xiàn)不一致性*/
       if (in_interrupt())
              return;

       local_irq_save(flags);  /*關(guān)中斷*/

       pending = local_softirq_pending(); /*取得per-cpu變量irq_stat本地cpu副本的__softirq_pending的值*/

       if (pending) /*如果有本地cpu有softirq掛起需要處理,則通過__do_softirq()運(yùn)行之,否則恢復(fù)中斷并退出*/
              __do_softirq();

       local_irq_restore(flags);  /*恢復(fù)開中斷*/
}

關(guān)鍵是__do_softirq()
asmlinkage void __do_softirq(void)
{
       struct softirq_action *h;
       __u32 pending;
       int max_restart = MAX_SOFTIRQ_RESTART;
       int cpu;

       pending = local_softirq_pending();

       local_bh_disable();
       cpu = smp_processor_id();
restart:
       /* Reset the pending bitmask before enabling irqs */
        /*把per-cpu變量 irq_stat的本地cpu副本的__softirq_pengding字段清0,
        表示代碼有信心在這一次處理中把本地cpu上掛起的所有softirq都處理掉(有信心只是開個(gè)玩笑;)*/
       set_softirq_pending(0);

       local_irq_enable(); /*保證下半部要在中斷打開的情況下進(jìn)行,否則下半部就失去意義了*/

       h = softirq_vec; /*softirq_vec是一個(gè)全局?jǐn)?shù)組(有32個(gè)元素),存放了32種softirq的處理函數(shù)*/

       /*遍歷unsigned int pengding的每一位,如果有被置為1,則運(yùn)行對(duì)應(yīng)的下半部處理函數(shù)action*/
       do {
              if (pending & 1) {
                       /*action是一個(gè)處理隊(duì)列,對(duì)于非tasklet的softirq來說只有一個(gè)元素,
                       但對(duì)于tasklet來說,就有N個(gè)函數(shù)需要處理*/
                     h->action(h);
                     rcu_bh_qsctr_inc(cpu);
              }
              h++;
              pending >>= 1;
       } while (pending);

       local_irq_disable();  /*關(guān)閉中斷*/
      
       /*因?yàn)橄掳氩渴窃陂_中斷的環(huán)境中運(yùn)行的,
       所以有可能在運(yùn)行了softirq A以后,
       然后在運(yùn)行其他的softirq B,
       這時(shí)又產(chǎn)生A的硬件中斷(A和B在同一個(gè)cpu中產(chǎn)生),
       而在A的上半部中又設(shè)置了per-cpu變量irq_stat的本地
       cpu副本的irq_stat的__softirq_pending的對(duì)應(yīng)的bit,
       所以代碼運(yùn)行到這里又發(fā)現(xiàn)__softirq_pending不0,
       所以要重做處理。這樣的情況最多執(zhí)行max_restart次,因?yàn)?br>       如果不限次數(shù)的運(yùn)行下去,中斷就一直不返回,那么進(jìn)程
       就得不到調(diào)度,系統(tǒng)性能會(huì)大大受影響。所以運(yùn)行max_restart次以后,
       如果這樣的情況還在一直發(fā)生,那么就喚醒per-cpu thread來專門執(zhí)行
       這些下半部,注意,在per-cpu thread中處理的中斷下半部,是可以睡眠
       的,但是編程人員無法掌握他編寫的softirq處理程序是在irq_exit()中處理還是
       在per-cpu thread中處理,所以一般都不會(huì)有睡眠的可能(編程人員需要保證
       這一點(diǎn))*/
       pending = local_softirq_pending();
       if (pending && --max_restart)
              goto restart;

       if (pending)
              wakeup_softirqd();

       __local_bh_enable(); /*enable下半部運(yùn)行*/
}

這里就不畫流程圖了,關(guān)鍵是要仔細(xì)的分析幾個(gè)中斷關(guān)閉和中斷使能的時(shí)機(jī),以及softirq是否可以重入的問題。

三、總結(jié)
本文分析了softirq運(yùn)行的時(shí)間點(diǎn),以及softirq是怎樣被cpu調(diào)度的。后面還要繼續(xù)分析tasklet的實(shí)現(xiàn),tasklet實(shí)際上就是凌駕在softirq機(jī)制上的,它占用了Linux現(xiàn)有6種softirq的2種(優(yōu)先級(jí)最高的和優(yōu)先級(jí)最低的)。
要特別注意的是:softirq處理函數(shù)也不能睡眠,因?yàn)樗彩沁\(yùn)行在中斷上下文環(huán)境中的(不考慮ksoftirqd線程)。



本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
linux UART串口驅(qū)動(dòng)開發(fā)文檔
內(nèi)核隨記(一)——理解中斷(3)
linux中斷源碼分析4/4--軟中斷
linux kernel的中斷子系統(tǒng)之(八):softirq
【原創(chuàng)】Linux中斷子系統(tǒng)(三)-softirq和tasklet
詳解Linux中斷處理中的hardirq與softirq機(jī)制
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服