linux內(nèi)核軟中斷
中斷的作用:當(dāng)一個(gè)中斷信號(hào)到達(dá)時(shí),CPU必須停止它當(dāng)前正做的工作,轉(zhuǎn)而去做中斷要求其做的事情。
中斷分為同步中斷和異步中斷兩種。
1、同步中斷又稱(chēng)異常,是由CPU執(zhí)行指令時(shí)由CPU控制單元產(chǎn)生的。異常又分兩種:
(1)、 一種是由程序執(zhí)行出錯(cuò)造成的,內(nèi)核通過(guò)發(fā)送一個(gè)unix的信號(hào)來(lái)處理異常。
(2)、一種是由內(nèi)核必須處理的異常條件產(chǎn)生的,比如缺頁(yè)異常,內(nèi)核執(zhí)行恢復(fù)異常的所有步驟。
2、異步中斷,通常我們就叫中斷。由其他硬件設(shè)備按照CPU時(shí)鐘信號(hào)隨機(jī)產(chǎn)生。
中斷處理程序的一般步驟:
一個(gè)中斷處理程序的幾個(gè)中斷服務(wù)例程之間是串行執(zhí)行的,并且在一個(gè)中斷處理程序結(jié)束前,不應(yīng)該再次出現(xiàn)這個(gè)中斷,所以一般中斷處理程序是先禁止該中斷,然后處理中斷,處理完成后在使能該中斷。
有一些中斷是可以延遲處理的,這種可延遲中斷可以在開(kāi)中斷的情況下執(zhí)行,執(zhí)行時(shí)允許其他中斷搶占他。把可延遲中斷從中斷處理程序中抽出來(lái)有助于使內(nèi)核保持較短的響應(yīng)時(shí)間。
Linux內(nèi)核使用三種方法來(lái)處理這種可延遲的中斷任務(wù):可延遲函數(shù)(軟中斷和tasklets)以及工作隊(duì)列。工作隊(duì)列是工作在進(jìn)程上下文中,可以睡眠,軟中斷和tasklets 是工作在中斷上下文,不可以睡眠。本節(jié)只討論軟中斷和tasklets。
軟中斷:
Linux 2.6 版本使用如下幾個(gè)軟中斷,不同版本之間略有差異。但一下幾個(gè)不同版本都包含。
HI_SOFTIRQ=0, 處理高優(yōu)先級(jí)的tasklet
TIMER_SOFTIRQ, 時(shí)鐘中斷相關(guān)的tasklet.
NET_TX_SOFTIRQ, 內(nèi)核把數(shù)據(jù)報(bào)文傳送給網(wǎng)卡。
NET_RX_SOFTIRQ, 內(nèi)核從網(wǎng)卡接收數(shù)據(jù)報(bào)文。
TASKLET_SOFTIRQ, 處理常規(guī)tasklet。
低下標(biāo)代表高優(yōu)先級(jí)。
內(nèi)核中定義了softirq_vec數(shù)組來(lái)存放各種軟中斷。定義如下:
static struct softirq_action softirq_vec[32]__cacheline_aligned_in_smp;
數(shù)組元素為 softirq_action,一個(gè)元素代碼一個(gè)軟中斷。不同的軟中斷號(hào)對(duì)應(yīng)不同的數(shù)組的下標(biāo)。
struct softirq_action
{
void (*action)(struct softirq_action *); //軟中斷發(fā)生時(shí)執(zhí)行軟中斷的處理函數(shù)。
void *data; //軟中斷的處理函數(shù)的參數(shù)指針。
};
初始化軟中斷時(shí)調(diào)用函數(shù) open_softirq()。如下代碼。
void open_softirq(int nr, void (*action)(struct softirq_action*),void *data)
{
softirq_vec[nr].data = data;
softirq_vec[nr].action = action;
}
另外一個(gè)跟軟中斷相關(guān)的關(guān)鍵字段是 32 位的 preempt_counte字段,用它來(lái)跟蹤內(nèi)核搶占和內(nèi)核控制路徑的嵌套,該字段放在每個(gè)進(jìn)程描述符的 thread_info 字段中。用函數(shù)preempt_count()來(lái)返回該字段的值。
preempt_count字段
位描述
0~7搶占計(jì)數(shù)器,記錄顯示禁用本地cpu內(nèi)核搶占的次數(shù),值為0時(shí)代表內(nèi)核允許搶占。
8~15軟中斷計(jì)數(shù)器。記錄軟中斷被禁用的次數(shù),0表示軟中斷被激活。
16~27硬中斷計(jì)數(shù)器。記錄硬中斷嵌套的層數(shù)。irq_entry()增加它的值,irq_exit()遞減它的值。
28
當(dāng)內(nèi)核明確不允許發(fā)生搶占或內(nèi)核正在中斷上下文中運(yùn)行時(shí),必須禁止內(nèi)核的搶占功能。為了確定當(dāng)前進(jìn)程是否能夠被搶占,內(nèi)核快速檢查preempt_counte字段是否等于零。
另一個(gè)跟軟中斷相關(guān)的字段是每個(gè)CPU都有一個(gè)32位掩碼的字段
typedef struct {
unsigned int __softirq_pending;
} ____cacheline_aligned irq_cpustat_t;
他描述掛起的軟中斷。每一位對(duì)應(yīng)相應(yīng)的軟中斷。比如0位代表HI_SOFTIRQ.
宏local_softirq_pending()來(lái)獲取該字段的值。
使用函數(shù)raise_softirq()來(lái)激活軟中斷。即把響應(yīng)的軟中斷號(hào)對(duì)應(yīng)的__softirq_pending中的位置1.表示該軟中斷被掛起。如果當(dāng)前CPU不在中斷上下文中,喚醒內(nèi)核線(xiàn)程ksoftirqd來(lái)檢查被掛起的軟中斷,然后執(zhí)行相應(yīng)軟中斷處理函數(shù)。
內(nèi)核在如下幾個(gè)點(diǎn)上檢查被掛起的軟中斷:
1、當(dāng)調(diào)用local_bh_enable()函數(shù)激活本地CPU的軟中斷時(shí)。條件滿(mǎn)足就調(diào)用do_softirq() 來(lái)處理軟中斷。
2、當(dāng)do_IRQ()完成硬中斷處理時(shí)調(diào)用irq_exit()時(shí)調(diào)用do_softirq()來(lái)處理軟中斷。
3、當(dāng)一個(gè)特殊內(nèi)核線(xiàn)程ksoftirq/n被喚醒時(shí),處理軟中斷。
軟中斷處理函數(shù)詳解:
點(diǎn)擊(此處)折疊或打開(kāi)asmlinkage void do_softirq(void)
{
__u32 pending;
unsigned long flags;
/*如果當(dāng)前處于硬中斷中,在硬中斷處理函數(shù)退出時(shí)會(huì)調(diào)用irq_exit()函數(shù)來(lái)處理軟中斷,
或當(dāng)前軟中斷被禁用。所以in_interrupt()返回不為1 就沒(méi)必要處理軟中斷,直接返回*/
if (in_interrupt())
return;
/*保持中斷寄存器的狀態(tài)并禁用本地CPU的中斷*/
local_irq_save(flags);
/*取得當(dāng)前cpu上__softirq_pending字段,獲取本地CPU上掛起的軟中斷*/
pending = local_softirq_pending();
/*如果當(dāng)前CPU上有掛起的軟中斷,執(zhí)行__do_softirq()來(lái)處理軟中斷*/
if (pending)
{
__do_softirq();
}
/*恢復(fù)中斷寄存器的狀態(tài)*/
local_irq_restore(flags);
}
asmlinkage void __do_softirq(void)
{
struct softirq_action *h;
__u32 pending;
int max_restart = MAX_SOFTIRQ_RESTART; //10
int cpu;
/*取得當(dāng)前cpu上__softirq_pending字段,獲取本地CPU上掛起的軟中斷*/
pending = local_softirq_pending();
/*debug 用,不討論*/
account_system_vtime(current);
/*禁止本地cpu的軟中斷,現(xiàn)在本地cpu上掛起的軟中斷已經(jīng)存入pending臨時(shí)變量中了*/
__local_bh_disable((unsigned long)__builtin_return_address(0));
/*debug 用,不討論*/
trace_softirq_enter();
/*取本地cpu id 號(hào)*/
cpu = smp_processor_id();
restart:
/* Reset the pending bitmask before enabling irqs */
/*清空本地cpu的__softirq_pending字段*/
set_softirq_pending(0);
/*開(kāi)啟本地cpu的硬中斷*/
local_irq_enable();
/*循環(huán)執(zhí)行被掛起的軟中斷處理函數(shù)。相應(yīng)的軟中斷的處理函數(shù)存在數(shù)組softirq_ver[nr]中的元素 softirq_action-》action中*/
h = softirq_vec;
do {
if (pending & 1) {
h-》action(h);
rcu_bh_qsctr_inc(cpu);
}
h++;
pending 》》= 1;
} while (pending);
/*禁止本地CPU的硬中斷*/
local_irq_disable();
/*取本地CPU的__softirq_pending,查看是否還有新的被掛起的軟中斷并且檢查被掛起軟中斷的次數(shù)小于10次,如果條件滿(mǎn)足,
繼續(xù)處理新的被掛起的軟中斷*/
pending = local_softirq_pending();
if (pending && --max_restart)
goto restart;
/*如果有新的掛起的軟中斷并且處理循環(huán)次數(shù)已經(jīng)夠了10次,
喚醒ksoftirq內(nèi)核線(xiàn)程來(lái)處理軟中斷*/
if (pending)
wakeup_softirqd();
/*debug 用,不討論*/
trace_softirq_exit();
account_system_vtime(current);
/*使能本地CPU的軟中斷*/
_local_bh_enable();
}
Linux內(nèi)核結(jié)構(gòu)
Linux內(nèi)核由七個(gè)部分構(gòu)成,具體如下圖:
a) 系統(tǒng)調(diào)用接口(SCI):open、read、write等系統(tǒng)調(diào)用
b) 進(jìn)程管理(PM):創(chuàng)建進(jìn)程、刪除進(jìn)程、調(diào)度進(jìn)程等
c) 內(nèi)存管理(MM):內(nèi)存分配、管理等
d) 虛擬文件系統(tǒng)(VFS):為多種文件系統(tǒng)提供統(tǒng)一的操作接口
e) 網(wǎng)絡(luò)協(xié)議棧:提供各種網(wǎng)絡(luò)協(xié)議
f) CPU架構(gòu)相關(guān)代碼(Arch):為的是提高至移植性
g) 設(shè)備驅(qū)動(dòng)程序(DD):各種設(shè)備驅(qū)動(dòng),占到內(nèi)核的70%左右代碼
linux內(nèi)核源碼詳解
1. 源碼獲取
Linux內(nèi)核獲取有兩種方法,一種是在www.kernel.org 直接獲取,另一種是使用git獲取(具體方法參考網(wǎng)絡(luò))。
2. 源碼目錄簡(jiǎn)介
其源碼主要有以下目錄(介紹重要目錄):
a) Arch目錄:存放處理器相關(guān)的代碼。下設(shè)子目錄,分別對(duì)應(yīng)具體的CPU,每個(gè)子目錄有boot,mm,以及kernel三個(gè)子目錄,分別對(duì)應(yīng)系統(tǒng)引導(dǎo)以及存儲(chǔ)管理,和系統(tǒng)調(diào)用
b) Include目錄:內(nèi)核所需要的大部分頭文件目錄。與平臺(tái)無(wú)關(guān)的在include/linux子目錄下,與平臺(tái)相關(guān)的則放在include相應(yīng)的子目錄中。
c) fs目錄:存放各種文件系統(tǒng)的實(shí)現(xiàn)代碼。
d) init目錄:init子目錄包含核心的初始化代碼(不是系統(tǒng)的引導(dǎo)代碼)。其包含兩個(gè)文件main.c和version.c,可以用來(lái)研究核心如何工作。
e) ipc目錄:包含核心進(jìn)程間的通信代碼。
f) kernel目錄:包含內(nèi)核管理的核心代碼。與硬件相關(guān)代碼放在arch/*/kernel目錄下。
g) mm目錄:包含了所有的內(nèi)存管理代碼。與硬件相關(guān)的內(nèi)存管理代碼位于arch/*/mm目錄下。
h) scripts目錄:包含用于配置核心的腳本文件。
i) lib目錄:包含了核心的庫(kù)代碼,與硬件相關(guān)的庫(kù)代碼被放在arch/*/lib/目錄下
評(píng)論