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

歡迎訪問 生活随笔!

生活随笔

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

linux

linux cpu softirq,linux softirq机制

發(fā)布時(shí)間:2023/12/4 linux 59 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux cpu softirq,linux softirq机制 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Copyright ? 2003 by 詹榮開

E-mail:zhanrk@sohu.com

Linux-2.4.0

Version 1.0.0,2003-2-14

摘要:本文主要從內(nèi)核實(shí)現(xiàn)的角度分析了Linux 2.4.0內(nèi)核的Softirq機(jī)制。本文是為那些想要了解Linux I/O子系統(tǒng)的讀者和Linux驅(qū)動程序開發(fā)人員而寫的。

關(guān)鍵詞:Linux、Softirq、軟中斷、Bottom half、設(shè)備驅(qū)動程序

申明:這份文檔是按照自由軟件開放源代碼的精神發(fā)布的,任何人可以免費(fèi)獲得、使用和重新發(fā)布,但是你沒有限制別人重新發(fā)布你發(fā)布內(nèi)容的權(quán)利。發(fā)布本文的目的是希望它能對讀者有用,但沒有任何擔(dān)保,甚至沒有適合特定目的的隱含的擔(dān)保。更詳細(xì)的情況請參閱GNU通用公共許可證(GPL),以及GNU自由文檔協(xié)議(GFDL)。

你應(yīng)該已經(jīng)和文檔一起收到一份GNU通用公共許可證(GPL)的副本。如果還沒有,寫信給:

The Free Software Foundation, Inc., 675 Mass Ave, Cambridge,MA02139, USA

歡迎各位指出文檔中的錯誤與疑問。

前言

中斷服務(wù)程序往往都是在CPU關(guān)中斷的條件下執(zhí)行的,以避免中斷嵌套而使控制復(fù)雜化。但是CPU關(guān)中斷的時(shí)間不能太長,否則容易丟失中斷信號。為此,Linux將中斷服務(wù)程序一分為二,各稱作“Top Half”和“Bottom Half”。前者通常對時(shí)間要求較為嚴(yán)格,必須在中斷請求發(fā)生后立即或至少在一定的時(shí)間限制內(nèi)完成。因此為了保證這種處理能原子地完成,Top Half通常是在CPU關(guān)中斷的條件下執(zhí)行的。具體地說,Top Half的范圍包括:從在IDT中登記的中斷入口函數(shù)一直到驅(qū)動程序注冊在中斷服務(wù)隊(duì)列中的ISR。而Bottom Half則是Top Half根據(jù)需要來調(diào)度執(zhí)行的,這些操作允許延遲到稍后執(zhí)行,它的時(shí)間要求并不嚴(yán)格,因此它通常是在CPU開中斷的條件下執(zhí)行的。

但是,Linux的這種Bottom Half(以下簡稱BH)機(jī)制有兩個缺點(diǎn),也即:(1)在任意一時(shí)刻,系統(tǒng)只能有一個CPU可以執(zhí)行Bottom Half代碼,以防止兩個或多個CPU同時(shí)來執(zhí)行Bottom Half函數(shù)而相互干擾。因此BH代碼的執(zhí)行是嚴(yán)格“串行化”的。(2)BH函數(shù)不允許嵌套。

這兩個缺點(diǎn)在單CPU系統(tǒng)中是無關(guān)緊要的,但在SMP系統(tǒng)中卻是非常致命的。因?yàn)锽H機(jī)制的嚴(yán)格串行化執(zhí)行顯然沒有充分利用SMP系統(tǒng)的多CPU特點(diǎn)。為此,Linux2.4內(nèi)核在BH機(jī)制的基礎(chǔ)上進(jìn)行了擴(kuò)展,這就是所謂的“軟中斷請求”(softirq)機(jī)制。

6.1 軟中斷請求機(jī)制

Linux的softirq機(jī)制是與SMP緊密不可分的。為此,整個softirq機(jī)制的設(shè)計(jì)與實(shí)現(xiàn)中自始自終都貫徹了一個思想:“誰觸發(fā),誰執(zhí)行”(Who marks,Who runs),也即觸發(fā)軟中斷的那個CPU負(fù)責(zé)執(zhí)行它所觸發(fā)的軟中斷,而且每個CPU都由它自己的軟中斷觸發(fā)與控制機(jī)制。這個設(shè)計(jì)思想也使得softirq 機(jī)制充分利用了SMP系統(tǒng)的性能和特點(diǎn)。

6.1.1 軟中斷描述符

Linux在include/linux/interrupt.h頭文件中定義了數(shù)據(jù)結(jié)構(gòu)softirq_action,來描述一個軟中斷請求,如下所示:

/* softirq mask and active fields moved to irq_cpustat_t in

* asm/hardirq.h to get better cache usage. KAO

*/

struct softirq_action

{

void (*action)(struct softirq_action *);

void *data;

};

其中,函數(shù)指針action指向軟中斷請求的服務(wù)函數(shù),而指針data則指向由服務(wù)函數(shù)自行解釋的數(shù)據(jù)。

基于上述軟中斷描述符,Linux在kernel/softirq.c文件中定義了一個全局的softirq_vec[32]數(shù)組:

static struct softirq_action softirq_vec[32] __cacheline_aligned;

在這里系統(tǒng)一共定義了32個軟中斷請求描述符。軟中斷向量i(0≤i≤31)所對應(yīng)的軟中斷請求描述符就是softirq_vec[i]。這個數(shù)組是個系統(tǒng)全局?jǐn)?shù)組,也即它被所有的CPU所共享。這里需要注意的一點(diǎn)是:每個CPU雖然都由它自己的觸發(fā)和控制機(jī)制,并且只執(zhí)行他自己所觸發(fā)的軟中斷請求,但是各個CPU所執(zhí)行的軟中斷服務(wù)例程卻是相同的,也即都是執(zhí)行softirq_vec[]數(shù)組中定義的軟中斷服務(wù)函數(shù)。

6.1.2 軟中斷觸發(fā)機(jī)制

要實(shí)現(xiàn)“誰觸發(fā),誰執(zhí)行”的思想,就必須為每個CPU都定義它自己的觸發(fā)和控制變量。為此,Linux在include/asm- i386/hardirq.h頭文件中定義了數(shù)據(jù)結(jié)構(gòu)irq_cpustat_t來描述一個CPU的中斷統(tǒng)計(jì)信息,其中就有用于觸發(fā)和控制軟中斷的成員變量。數(shù)據(jù)結(jié)構(gòu)irq_cpustat_t的定義如下:

/* entry.S is sensitive to the offsets of these fields */

typedef struct {

unsigned int __softirq_active;

unsigned int __softirq_mask;

unsigned int __local_irq_count;

unsigned int __local_bh_count;

unsigned int __syscall_count;

unsigned int __nmi_count; /* arch dependent */

} ____cacheline_aligned irq_cpustat_t;

結(jié)構(gòu)中每一個成員都是一個32位的無符號整數(shù)。其中__softirq_active和__softirq_mask就是用于觸發(fā)和控制軟中斷的成員變量。

①__softirq_active變量:32位的無符號整數(shù),表示軟中斷向量0~31的狀態(tài)。如果bit[i](0≤i≤31)為1,則表示軟中斷向量i在某個CPU上已經(jīng)被觸發(fā)而處于active狀態(tài);為0表示處于非活躍狀態(tài)。

②__softirq_mask變量:32位的無符號整數(shù),軟中斷向量的屏蔽掩碼。如果bit[i](0≤i≤31)為1,則表示使能(enable)軟中斷向量i,為0表示該軟中斷向量被禁止(disabled)。

根據(jù)系統(tǒng)中當(dāng)前的CPU個數(shù)(由宏NR_CPUS表示),Linux在kernel/softirq.c文件中為每個CPU都定義了它自己的中斷統(tǒng)計(jì)信息結(jié)構(gòu),如下所示:

/* No separate irq_stat for s390, it is part of PSA */

#if !defined(CONFIG_ARCH_S390)

irq_cpustat_t irq_stat[NR_CPUS];

#endif /* CONFIG_ARCH_S390 */

這樣,每個CPU都只操作它自己的中斷統(tǒng)計(jì)信息結(jié)構(gòu)。假設(shè)有一個編號為id的CPU,那么它只能操作它自己的中斷統(tǒng)計(jì)信息結(jié)構(gòu)irq_stat [id](0≤id≤NR_CPUS-1),從而使各CPU之間互不影響。這個數(shù)組在include/linux/irq_cpustat.h頭文件中也作了原型聲明。

l 觸發(fā)軟中斷請求的操作函數(shù)

函數(shù)__cpu_raise_softirq()用于在編號為cpu的處理器上觸發(fā)軟中斷向量nr。它通過將相應(yīng)的__softirq_active成員變量中的相應(yīng)位設(shè)置為1來實(shí)現(xiàn)軟中斷觸發(fā)。如下所示(include/linux/interrupt.h):

static inline void __cpu_raise_softirq(int cpu, int nr)

{

softirq_active(cpu) |= (1<

}

為了保證“原子”性地完成軟中斷的觸發(fā)過程,Linux在interrupt.h頭文件中對上述內(nèi)聯(lián)函數(shù)又作了高層封裝,也即函數(shù) raise_softirq()。該函數(shù)向下通過調(diào)用__cpu_raise_softirq()函數(shù)來實(shí)現(xiàn)軟中斷的觸發(fā),但在調(diào)用該函數(shù)之前,它先通過 local_irq_save()函數(shù)來關(guān)閉當(dāng)前CPU的中斷并保存標(biāo)志寄存器的內(nèi)容,如下所示:

/* I do not want to use atomic variables now, so that cli/sti */

static inline void raise_softirq(int nr)

{

unsigned long flags;

local_irq_save(flags);

__cpu_raise_softirq(smp_processor_id(), nr);

local_irq_restore(flags);

}

6.1.3 Linux對軟中斷的預(yù)定義分類

在軟中斷向量0~31中,Linux內(nèi)核僅僅使用了軟中斷向量0~3,其余被留待系統(tǒng)以后擴(kuò)展。Linux在頭文件include/linux/interrupt.h中對軟中斷向量0~3進(jìn)行了預(yù)定義:

/* 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,

NET_TX_SOFTIRQ,

NET_RX_SOFTIRQ,

TASKLET_SOFTIRQ

};

其中,軟中斷向量0(即HI_SOFTIRQ)用于實(shí)現(xiàn)高優(yōu)先級的軟中斷,如:高優(yōu)先級的tasklet(將在后面詳細(xì)描述)。軟中斷向量1和2 則分別用于網(wǎng)絡(luò)數(shù)據(jù)的發(fā)送與接收。軟中斷向量3(即TASKLET_SOFTIRQ)則用于實(shí)現(xiàn)諸如tasklet這樣的一般性軟中斷。關(guān)于 tasklet我們將在后面詳細(xì)描述。NOTE!Linix內(nèi)核并不鼓勵一般用戶擴(kuò)展使用剩余的軟中斷向量,因?yàn)樗J(rèn)為其預(yù)定義的軟中斷向量 HI_SOFTIRQ和TASKLET_SOFTIRQ已經(jīng)足夠應(yīng)付絕大多數(shù)應(yīng)用。

6.1.4 軟中斷機(jī)制的初始化

函數(shù)softirq_init()完成softirq機(jī)制的初始化。該函數(shù)由內(nèi)核啟動例程start_kernel()所調(diào)用。函數(shù)源碼如下所示(kernel/softirq.c):

void __init softirq_init()

{

int i;

for (i=0; i<32; i++)

tasklet_init(bh_task_vec+i, bh_action, i);

open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);

open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);

}

初始化的過程如下:

(1)先用一個for循環(huán)來初始化用于實(shí)現(xiàn)BH機(jī)制的bh_task_vec[32]數(shù)組。這一點(diǎn)我們將在后面詳細(xì)解釋。

(2)調(diào)用open_softirq()函數(shù)開啟使用軟中斷向量TASKLET_SOFTIRQ和HI_SOFTIRQ,并將它們的軟中斷服務(wù)函數(shù)指針分別指向tasklet_action()函數(shù)和tasklet_hi_action()函數(shù)。函數(shù)open_softirq()的主要作用是初始化設(shè)置軟中斷請求描述符softirq_vec[nr]。

6.1.5 開啟一個指定的軟中斷向量

函數(shù)open_softirq()用于開啟一個指定的軟中斷向量nr,也即適當(dāng)?shù)爻跏蓟浿袛嘞蛄縩r所對應(yīng)的軟中斷描述符 softirq_vec[nr]。它主要做兩件事情:(1)初始化設(shè)置軟中斷向量nr所對應(yīng)的軟中斷描述符softirq_vec[nr]。(2)將所有 CPU的軟中斷屏蔽掩碼變量__softirq_mask中的對應(yīng)位設(shè)置為1,以使能該軟中斷向量。該函數(shù)的源碼如下所示(kernel/softirq.c):

void open_softirq(int nr, void (*action)(struct softirq_action*), void *data)

{

unsigned long flags;

int i;

spin_lock_irqsave(&softirq_mask_lock, flags);

softirq_vec[nr].data = data;

softirq_vec[nr].action = action;

for (i=0; i

softirq_mask(i) |= (1<

spin_unlock_irqrestore(&softirq_mask_lock, flags);

}

6.1.6 軟中斷服務(wù)的執(zhí)行函數(shù)do_softirq()

函數(shù)do_softirq()負(fù)責(zé)執(zhí)行數(shù)組softirq_vec[32]中設(shè)置的軟中斷服務(wù)函數(shù)。每個CPU都是通過執(zhí)行這個函數(shù)來執(zhí)行軟中斷服務(wù)的。由于同一個CPU上的軟中斷服務(wù)例程不允許嵌套,因此,do_softirq()函數(shù)一開始就檢查當(dāng)前CPU是否已經(jīng)正出在中斷服務(wù)中,如果是則 do_softirq()函數(shù)立即返回。舉個例子,假設(shè)CPU0正在執(zhí)行do_softirq()函數(shù),執(zhí)行過程產(chǎn)生了一個高優(yōu)先級的硬件中斷,于是 CPU0轉(zhuǎn)去執(zhí)行這個高優(yōu)先級中斷所對應(yīng)的中斷服務(wù)程序??偹苤?#xff0c;所有的中斷服務(wù)程序最后都要跳轉(zhuǎn)到do_IRQ()函數(shù)并由它來依次執(zhí)行中斷服務(wù)隊(duì)列中的ISR,這里我們假定這個高優(yōu)先級中斷的ISR請求觸發(fā)了一次軟中斷,于是do_IRQ()函數(shù)在退出之前看到有軟中斷請求,從而調(diào)用 do_softirq()函數(shù)來服務(wù)軟中斷請求。因此,CPU0再次進(jìn)入do_softirq()函數(shù)(也即do_softirq()函數(shù)在CPU0上被重入了)。但是在這一次進(jìn)入do_softirq()函數(shù)時(shí),它馬上發(fā)現(xiàn)CPU0此前已經(jīng)處在中斷服務(wù)狀態(tài)中了,因此這一次do_softirq()函數(shù)立即返回。于是,CPU0回到該開始時(shí)的do_softirq()函數(shù)繼續(xù)執(zhí)行,并為高優(yōu)先級中斷的ISR所觸發(fā)的軟中斷請求補(bǔ)上一次服務(wù)。從這里可以看出,do_softirq()函數(shù)在同一個CPU上的執(zhí)行是串行的。

函數(shù)源碼如下(kernel/softirq.c):

asmlinkage void do_softirq()

{

int cpu = smp_processor_id();

__u32 active, mask;

if (in_interrupt())

return;

local_bh_disable();

local_irq_disable();

mask = softirq_mask(cpu);

active = softirq_active(cpu) & mask;

if (active) {

struct softirq_action *h;

restart:

/* Reset active bitmask before enabling irqs */

softirq_active(cpu) &= ~active;

local_irq_enable();

h = softirq_vec;

mask &= ~active;

do {

if (active & 1)

h->action(h);

h++;

active >>= 1;

} while (active);

local_irq_disable();

active = softirq_active(cpu);

if ((active &= mask) != 0)

goto retry;

}

local_bh_enable();

/* Leave with locally disabled hard irqs. It is critical to close

* window for infinite recursion, while we help local bh count,

* it protected us. Now we are defenceless.

*/

return;

retry:

goto restart;

}

結(jié)合上述源碼,我們可以看出軟中斷服務(wù)的執(zhí)行過程如下:

(1)調(diào)用宏in_interrupt()來檢測當(dāng)前CPU此次是否已經(jīng)處于中斷服務(wù)中。該宏定義在hardirq.h,請參見5.7節(jié)。

(2)調(diào)用local_bh_disable()宏將當(dāng)前CPU的中斷統(tǒng)計(jì)信息結(jié)構(gòu)中的__local_bh_count成員變量加1,表示當(dāng)前CPU已經(jīng)處在軟中斷服務(wù)狀態(tài)。

(3)由于接下來要讀寫當(dāng)前CPU的中斷統(tǒng)計(jì)信息結(jié)構(gòu)中的__softirq_active變量和__softirq_mask變量,因此為了保證這一個操作過程的原子性,先用local_irq_disable()宏(實(shí)際上就是cli指令)關(guān)閉當(dāng)前CPU的中斷。

(4)然后,讀當(dāng)前CPU的__softirq_active變量值和__softirq_mask變量值。當(dāng)某個軟中斷向量被觸發(fā)時(shí)(即 __softirq_active變量中的相應(yīng)位被置1),只有__softirq_mask變量中的相應(yīng)位也為1時(shí),它的軟中斷服務(wù)函數(shù)才能得到執(zhí)行。因此,需要將__softirq_active變量和__softirq_mask變量作一次“與”邏輯操作。

(5)如果active變量非0,說明需要執(zhí)行軟中斷服務(wù)函數(shù)。因此:①先將當(dāng)前CPU的__softirq_active中的相應(yīng)位清零,然后用local_irq_enable()宏(實(shí)際上就是sti指令)打開當(dāng)前CPU的中斷。②將局部變量mask中的相應(yīng)位清零,其目的是:讓 do_softirq()函數(shù)的這一次執(zhí)行不對同一個軟中斷向量上的再次軟中斷請求進(jìn)行服務(wù),而是將它留待下一次do_softirq()執(zhí)行時(shí)去服務(wù),從而使do_sottirq()函數(shù)避免陷入無休止的軟中斷服務(wù)中。③用一個do{}while循環(huán)來根據(jù)active的值去執(zhí)行相應(yīng)的軟中斷服務(wù)函數(shù)。 ④由于接下來又要檢測當(dāng)前CPU的__softirq_active變量,因此再一次調(diào)用local_irq_disable()宏關(guān)閉當(dāng)前CPU的中斷。⑤讀取當(dāng)前CPU的__softirq_active變量的值,并將它與局部變量mask進(jìn)行與操作,以看看是否又有其他軟中斷服務(wù)被觸發(fā)了(比如前面所說的那種情形)。如果有的話,那就跳轉(zhuǎn)到entry程序段(實(shí)際上是跳轉(zhuǎn)到restart程序段)重新執(zhí)行軟中斷服務(wù)。如果沒有的話,那么此次軟中斷服務(wù)過程就宣告結(jié)束。

(6)最后,通過local_bh_enable()宏將當(dāng)前CPU的__local_bh_count變量值減1,表示當(dāng)前CPU已經(jīng)離開軟中斷服務(wù)狀態(tài)。宏local_bh_enable()也定義在include/asm-i386/softirq.h頭文件中。

總結(jié)

以上是生活随笔為你收集整理的linux cpu softirq,linux softirq机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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