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