概述
從本質(zhì)上來(lái)講,中斷是一種電信號(hào),當(dāng)設(shè)備有某種事件發(fā)生時(shí),它就會(huì)產(chǎn)生中斷,通過(guò)總線把電信號(hào)發(fā)送給中斷控制器。
如果中斷的線是激活的,中斷控制器就把電信號(hào)發(fā)送給處理器的某個(gè)特定引腳。處理器于是立即停止自己正在做的事,
跳到中斷處理程序的入口點(diǎn),進(jìn)行中斷處理。
(1) 硬中斷
由與系統(tǒng)相連的外設(shè)(比如網(wǎng)卡、硬盤(pán))自動(dòng)產(chǎn)生的。主要是用來(lái)通知操作系統(tǒng)系統(tǒng)外設(shè)狀態(tài)的變化。比如當(dāng)網(wǎng)卡收到數(shù)據(jù)包
的時(shí)候,就會(huì)發(fā)出一個(gè)中斷。我們通常所說(shuō)的中斷指的是硬中斷(hardirq)。
(2) 軟中斷
為了滿(mǎn)足實(shí)時(shí)系統(tǒng)的要求,中斷處理應(yīng)該是越快越好。linux為了實(shí)現(xiàn)這個(gè)特點(diǎn),當(dāng)中斷發(fā)生的時(shí)候,硬中斷處理那些短時(shí)間
就可以完成的工作,而將那些處理事件比較長(zhǎng)的工作,放到中斷之后來(lái)完成,也就是軟中斷(softirq)來(lái)完成。
(3) 中斷嵌套
Linux下硬中斷是可以嵌套的,但是沒(méi)有優(yōu)先級(jí)的概念,也就是說(shuō)任何一個(gè)新的中斷都可以打斷正在執(zhí)行的中斷,但同種中斷
除外。軟中斷不能嵌套,但相同類(lèi)型的軟中斷可以在不同CPU上并行執(zhí)行。
(4) 軟中斷指令
int是軟中斷指令。
中斷向量表是中斷號(hào)和中斷處理函數(shù)地址的對(duì)應(yīng)表。
int n - 觸發(fā)軟中斷n。相應(yīng)的中斷處理函數(shù)的地址為:中斷向量表地址 + 4 * n。
(5)硬中斷和軟中斷的區(qū)別
軟中斷是執(zhí)行中斷指令產(chǎn)生的,而硬中斷是由外設(shè)引發(fā)的。
硬中斷的中斷號(hào)是由中斷控制器提供的,軟中斷的中斷號(hào)由指令直接指出,無(wú)需使用中斷控制器。
硬中斷是可屏蔽的,軟中斷不可屏蔽。
硬中斷處理程序要確保它能快速地完成任務(wù),這樣程序執(zhí)行時(shí)才不會(huì)等待較長(zhǎng)時(shí)間,稱(chēng)為上半部。
軟中斷處理硬中斷未完成的工作,是一種推后執(zhí)行的機(jī)制,屬于下半部。?
開(kāi)關(guān)
?
(1) 硬中斷的開(kāi)關(guān)
簡(jiǎn)單禁止和激活當(dāng)前處理器上的本地中斷:
local_irq_disable();
local_irq_enable();
保存本地中斷系統(tǒng)狀態(tài)下的禁止和激活:
unsigned long flags;
local_irq_save(flags);
local_irq_restore(flags);
(2) 軟中斷的開(kāi)關(guān)
禁止下半部,如softirq、tasklet和workqueue等:
local_bh_disable();
local_bh_enable();
需要注意的是,禁止下半部時(shí)仍然可以被硬中斷搶占。
(3) 判斷中斷狀態(tài)
#define in_interrupt() (irq_count())?// 是否處于中斷狀態(tài)(硬中斷或軟中斷)
#define in_irq()?(hardirq_count()) // 是否處于硬中斷
#define in_softirq() (softirq_count()) // 是否處于軟中斷
硬中斷
?
(1) 注冊(cè)中斷處理函數(shù)
注冊(cè)中斷處理函數(shù):
/**
* irq: 要分配的中斷號(hào)
* handler: 要注冊(cè)的中斷處理函數(shù)
* flags: 標(biāo)志(一般為0)
* name: 設(shè)備名(dev->name)
* dev: 設(shè)備(struct net_device *dev),作為中斷處理函數(shù)的參數(shù)
* 成功返回0
*/
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,?
const char *name, void *dev);
?
中斷處理函數(shù)本身:
typedef irqreturn_t (*irq_handler_t) (int, void *);
/**
* enum irqreturn
* @IRQ_NONE: interrupt was not from this device
* @IRQ_HANDLED: interrupt was handled by this device
* @IRQ_WAKE_THREAD: handler requests to wake the handler thread
*/
enum irqreturn {
IRQ_NONE,
IRQ_HANDLED,
IRQ_WAKE_THREAD,
};
typedef enum irqreturn irqreturn_t;
#define IRQ_RETVAL(x) ((x) != IRQ_NONE)
?
(2) 注銷(xiāo)中斷處理函數(shù)
/**
* free_irq - free an interrupt allocated with request_irq
* @irq: Interrupt line to free
* @dev_id: Device identity to free
*
* Remove an interrupt handler. The handler is removed and if the
* interrupt line is no longer in use by any driver it is disabled.
* On a shared IRQ the caller must ensure the interrupt is disabled
* on the card it drives before calling this function. The function does
* not return until any executing interrupts for this IRQ have completed.
* This function must not be called from interrupt context.
*/
void free_irq(unsigned int irq, void *dev_id);
?
軟中斷
?
(1) 定義
軟中斷是一組靜態(tài)定義的下半部接口,可以在所有處理器上同時(shí)執(zhí)行,即使兩個(gè)類(lèi)型相同也可以。
但一個(gè)軟中斷不會(huì)搶占另一個(gè)軟中斷,唯一可以搶占軟中斷的是硬中斷。
軟中斷由softirq_action結(jié)構(gòu)體表示:
struct softirq_action {
void (*action) (struct softirq_action *); /* 軟中斷的處理函數(shù) */
};
?
目前已注冊(cè)的軟中斷有10種,定義為一個(gè)全局?jǐn)?shù)組:
static struct softirq_action softirq_vec[NR_SOFTIRQS];
enum {
HI_SOFTIRQ = 0, /* 優(yōu)先級(jí)高的tasklets */
TIMER_SOFTIRQ, /* 定時(shí)器的下半部 */
NET_TX_SOFTIRQ, /* 發(fā)送網(wǎng)絡(luò)數(shù)據(jù)包 */
NET_RX_SOFTIRQ, /* 接收網(wǎng)絡(luò)數(shù)據(jù)包 */
BLOCK_SOFTIRQ, /* BLOCK裝置 */
BLOCK_IOPOLL_SOFTIRQ,
TASKLET_SOFTIRQ, /* 正常優(yōu)先級(jí)的tasklets */
SCHED_SOFTIRQ, /* 調(diào)度程序 */
HRTIMER_SOFTIRQ, /* 高分辨率定時(shí)器 */
RCU_SOFTIRQ, /* RCU鎖定 */
NR_SOFTIRQS /* 10 */
};
?
(2) 注冊(cè)軟中斷處理函數(shù)
/**
* @nr: 軟中斷的索引號(hào)
* @action: 軟中斷的處理函數(shù)
*/
void open_softirq(int nr, void (*action) (struct softirq_action *))
{
softirq_vec[nr].action = action;
}
例如:
open_softirq(NET_TX_SOFTIRQ, net_tx_action);
open_softirq(NET_RX_SOFTIRQ, net_rx_action);
(3) 觸發(fā)軟中斷?
調(diào)用raise_softirq()來(lái)觸發(fā)軟中斷。
void raise_softirq(unsigned int nr)
{
unsigned long flags;
local_irq_save(flags);
raise_softirq_irqoff(nr);
local_irq_restore(flags);
}
/* This function must run with irqs disabled */
inline void rasie_softirq_irqsoff(unsigned int nr)
{
__raise_softirq_irqoff(nr);
/* If we're in an interrupt or softirq, we're done
* (this also catches softirq-disabled code). We will
* actually run the softirq once we return from the irq
* or softirq.
* Otherwise we wake up ksoftirqd to make sure we
* schedule the softirq soon.
*/
if (! in_interrupt()) /* 如果不處于硬中斷或軟中斷 */
wakeup_softirqd(void); /* 喚醒ksoftirqd/n進(jìn)程 */
}
Percpu變量irq_cpustat_t中的__softirq_pending是等待處理的軟中斷的位圖,通過(guò)設(shè)置此變量
即可告訴內(nèi)核該執(zhí)行哪些軟中斷。
static inline void __rasie_softirq_irqoff(unsigned int nr)
{
trace_softirq_raise(nr);
or_softirq_pending(1UL << nr);
}
typedef struct {
unsigned int __softirq_pending;
unsigned int __nmi_count; /* arch dependent */
} irq_cpustat_t;
irq_cpustat_t irq_stat[];
#define __IRQ_STAT(cpu, member) (irq_stat[cpu].member)
#define or_softirq_pending(x) percpu_or(irq_stat.__softirq_pending, (x))
#define local_softirq_pending() percpu_read(irq_stat.__softirq_pending)
?
喚醒ksoftirqd內(nèi)核線程處理軟中斷。
static void wakeup_softirqd(void)
{
/* Interrupts are disabled: no need to stop preemption */
struct task_struct *tsk = __get_cpu_var(ksoftirqd);
if (tsk && tsk->state != TASK_RUNNING)
wake_up_process(tsk);
}
?
在下列地方,待處理的軟中斷會(huì)被檢查和執(zhí)行:
1. 從一個(gè)硬件中斷代碼處返回時(shí)
2. 在ksoftirqd內(nèi)核線程中
3. 在那些顯示檢查和執(zhí)行待處理的軟中斷的代碼中,如網(wǎng)絡(luò)子系統(tǒng)中
而不管是用什么方法喚起,軟中斷都要在do_softirq()中執(zhí)行。如果有待處理的軟中斷,
do_softirq()會(huì)循環(huán)遍歷每一個(gè),調(diào)用它們的相應(yīng)的處理程序。
在中斷處理程序中觸發(fā)軟中斷是最常見(jiàn)的形式。中斷處理程序執(zhí)行硬件設(shè)備的相關(guān)操作,
然后觸發(fā)相應(yīng)的軟中斷,最后退出。內(nèi)核在執(zhí)行完中斷處理程序以后,馬上就會(huì)調(diào)用
do_softirq(),于是軟中斷開(kāi)始執(zhí)行中斷處理程序完成剩余的任務(wù)。
下面來(lái)看下do_softirq()的具體實(shí)現(xiàn)。
asmlinkage void do_softirq(void)
{
__u32 pending;
unsigned long flags;
/* 如果當(dāng)前已處于硬中斷或軟中斷中,直接返回 */
if (in_interrupt())?
return;
local_irq_save(flags);
pending = local_softirq_pending();
if (pending) /* 如果有激活的軟中斷 */
__do_softirq(); /* 處理函數(shù) */
local_irq_restore(flags);
}
/* We restart softirq processing MAX_SOFTIRQ_RESTART times,
* and we fall back to softirqd after that.
* This number has been established via experimentation.
* The two things to balance is latency against fairness - we want
* to handle softirqs as soon as possible, but they should not be
* able to lock up the box.
*/
asmlinkage void __do_softirq(void)
{
struct softirq_action *h;
__u32 pending;
/* 本函數(shù)能重復(fù)觸發(fā)執(zhí)行的次數(shù),防止占用過(guò)多的cpu時(shí)間 */
int max_restart = MAX_SOFTIRQ_RESTART;
int cpu;
pending = local_softirq_pending(); /* 激活的軟中斷位圖 */
account_system_vtime(current);
/* 本地禁止當(dāng)前的軟中斷 */
__local_bh_disable((unsigned long)__builtin_return_address(0), SOFTIRQ_OFFSET);
lockdep_softirq_enter(); /* current->softirq_context++ */
cpu = smp_processor_id(); /* 當(dāng)前cpu編號(hào) */
restart:
/* Reset the pending bitmask before enabling irqs */
set_softirq_pending(0); /* 重置位圖 */
local_irq_enable();
h = softirq_vec;
do {
if (pending & 1) {
unsigned int vec_nr = h - softirq_vec; /* 軟中斷索引 */
int prev_count = preempt_count();
kstat_incr_softirqs_this_cpu(vec_nr);
trace_softirq_entry(vec_nr);
h->action(h); /* 調(diào)用軟中斷的處理函數(shù) */
trace_softirq_exit(vec_nr);
if (unlikely(prev_count != preempt_count())) {
printk(KERN_ERR "huh, entered softirq %u %s %p" "with preempt_count %08x,"
"exited with %08x?
", vec_nr, softirq_to_name[vec_nr], h->action, prev_count,
preempt_count());
}
rcu_bh_qs(cpu);
}
h++;
pending >>= 1;
} while(pending);
local_irq_disable();
pending = local_softirq_pending();
if (pending & --max_restart) /* 重復(fù)觸發(fā) */
goto restart;
/* 如果重復(fù)觸發(fā)了10次了,接下來(lái)喚醒ksoftirqd/n內(nèi)核線程來(lái)處理 */
if (pending)
wakeup_softirqd();?
lockdep_softirq_exit();
account_system_vtime(current);
__local_bh_enable(SOFTIRQ_OFFSET);
}
?
(4) ksoftirqd內(nèi)核線程
內(nèi)核不會(huì)立即處理重新觸發(fā)的軟中斷。
當(dāng)大量軟中斷出現(xiàn)的時(shí)候,內(nèi)核會(huì)喚醒一組內(nèi)核線程來(lái)處理。
這些線程的優(yōu)先級(jí)最低(nice值為19),這能避免它們跟其它重要的任務(wù)搶奪資源。
但它們最終肯定會(huì)被執(zhí)行,所以這個(gè)折中的方案能夠保證在軟中斷很多時(shí)用戶(hù)程序不會(huì)
因?yàn)榈貌坏教幚頃r(shí)間而處于饑餓狀態(tài),同時(shí)也保證過(guò)量的軟中斷最終會(huì)得到處理。
每個(gè)處理器都有一個(gè)這樣的線程,名字為ksoftirqd/n,n為處理器的編號(hào)。
static int run_ksoftirqd(void *__bind_cpu)
{
set_current_state(TASK_INTERRUPTIBLE);
current->flags |= PF_KSOFTIRQD; /* I am ksoftirqd */
while(! kthread_should_stop()) {
preempt_disable();
if (! local_softirq_pending()) { /* 如果沒(méi)有要處理的軟中斷 */
preempt_enable_no_resched();
schedule();
preempt_disable():
}
__set_current_state(TASK_RUNNING);
while(local_softirq_pending()) {
/* Preempt disable stops cpu going offline.
* If already offline, we'll be on wrong CPU: don't process.
*/
if (cpu_is_offline(long)__bind_cpu))/* 被要求釋放cpu */
goto wait_to_die;
do_softirq(); /* 軟中斷的統(tǒng)一處理函數(shù) */
preempt_enable_no_resched();
cond_resched();
preempt_disable();
rcu_note_context_switch((long)__bind_cpu);
}
preempt_enable();
set_current_state(TASK_INTERRUPTIBLE);
}
__set_current_state(TASK_RUNNING);
return 0;
wait_to_die:
preempt_enable();
/* Wait for kthread_stop */
set_current_state(TASK_INTERRUPTIBLE);
while(! kthread_should_stop()) {
schedule();
set_current_state(TASK_INTERRUPTIBLE);
}
__set_current_state(TASK_RUNNING);
return 0;
}
?
評(píng)論
查看更多