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

打開(kāi)APP
userphoto
未登錄

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

開(kāi)通VIP
Linux中斷(interrupt)子系統(tǒng)之五:軟件中斷(softIRQ)

軟件中斷(softIRQ)是內(nèi)核提供的一種延遲執(zhí)行機(jī)制,它完全由軟件觸發(fā),雖然說(shuō)是延遲機(jī)制,實(shí)際上,在大多數(shù)情況下,它與普通進(jìn)程相比,能得到更快的響應(yīng)時(shí)間。軟中斷也是其他一些內(nèi)核機(jī)制的基礎(chǔ),比如tasklet,高分辨率timer等。

/*****************************************************************************************************/
聲明:本博內(nèi)容均由http://blog.csdn.net/droidphone原創(chuàng),轉(zhuǎn)載請(qǐng)注明出處,謝謝!
/*****************************************************************************************************/

1.  軟件中斷的數(shù)據(jù)結(jié)構(gòu)


1.1  struct softirq_action


        內(nèi)核用softirq_action結(jié)構(gòu)管理軟件中斷的注冊(cè)和激活等操作,它的定義如下:
  1. struct softirq_action  
  2. {  
  3.     void    (*action)(struct softirq_action *);  
  4. };  
非常簡(jiǎn)單,只有一個(gè)用于回調(diào)的函數(shù)指針。軟件中斷的資源是有限的,內(nèi)核目前只實(shí)現(xiàn)了10種類型的軟件中斷,它們是:
  1. enum  
  2. {  
  3.     HI_SOFTIRQ=0,  
  4.     TIMER_SOFTIRQ,  
  5.     NET_TX_SOFTIRQ,  
  6.     NET_RX_SOFTIRQ,  
  7.     BLOCK_SOFTIRQ,  
  8.     BLOCK_IOPOLL_SOFTIRQ,  
  9.     TASKLET_SOFTIRQ,  
  10.     SCHED_SOFTIRQ,  
  11.     HRTIMER_SOFTIRQ,  
  12.     RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */  
  13.   
  14.     NR_SOFTIRQS  
  15. };  
內(nèi)核的開(kāi)發(fā)者們不建議我們擅自增加軟件中斷的數(shù)量,如果需要新的軟件中斷,盡可能把它們實(shí)現(xiàn)為基于軟件中斷的tasklet形式。與上面的枚舉值相對(duì)應(yīng),內(nèi)核定義了一個(gè)softirq_action的結(jié)構(gòu)數(shù)組,每種軟中斷對(duì)應(yīng)數(shù)組中的一項(xiàng):
  1. static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;  


1.2  irq_cpustat_t


        多個(gè)軟中斷可以同時(shí)在多個(gè)cpu運(yùn)行,就算是同一種軟中斷,也有可能同時(shí)在多個(gè)cpu上運(yùn)行。內(nèi)核為每個(gè)cpu都管理著一個(gè)待決軟中斷變量(pending),它就是irq_cpustat_t:
  1. typedef struct {  
  2.     unsigned int __softirq_pending;  
  3. } ____cacheline_aligned irq_cpustat_t;  
  1. irq_cpustat_t irq_stat[NR_CPUS] ____cacheline_aligned;  
__softirq_pending字段中的每一個(gè)bit,對(duì)應(yīng)著某一個(gè)軟中斷,某個(gè)bit被置位,說(shuō)明有相應(yīng)的軟中斷等待處理。


1.3  軟中斷的守護(hù)進(jìn)程ksoftirqd


        在cpu的熱插拔階段,內(nèi)核為每個(gè)cpu創(chuàng)建了一個(gè)用于執(zhí)行軟件中斷的守護(hù)進(jìn)程ksoftirqd,同時(shí)定義了一個(gè)per_cpu變量用于保存每個(gè)守護(hù)進(jìn)程的task_struct結(jié)構(gòu)指針:
  1. DEFINE_PER_CPU(struct task_struct *, ksoftirqd);  
大多數(shù)情況下,軟中斷都會(huì)在irq_exit階段被執(zhí)行,在irq_exit階段沒(méi)有處理完的軟中斷才有可能會(huì)在守護(hù)進(jìn)程中執(zhí)行。


2.  觸發(fā)軟中斷


        要觸發(fā)一個(gè)軟中斷,只要調(diào)用api:raise_softirq即可,它的實(shí)現(xiàn)很簡(jiǎn)單,先是關(guān)閉本地cpu中斷,然后調(diào)用:raise_softirq_irqoff
  1. void raise_softirq(unsigned int nr)  
  2. {  
  3.     unsigned long flags;  
  4.   
  5.     local_irq_save(flags);  
  6.     raise_softirq_irqoff(nr);  
  7.     local_irq_restore(flags);  
  8. }  
再看看raise_softirq_irqoff:
  1. inline void raise_softirq_irqoff(unsigned int nr)  
  2. {  
  3.     __raise_softirq_irqoff(nr);  
  4.   
  5.         ......  
  6.     if (!in_interrupt())  
  7.         wakeup_softirqd();  
  8. }  
先是通過(guò)__raise_softirq_irqoff設(shè)置cpu的軟中斷pending標(biāo)志位(irq_stat[NR_CPUS] ),然后通過(guò)in_interrupt判斷現(xiàn)在是否在中斷上下文中,或者軟中斷是否被禁止,如果都不成立,則喚醒軟中斷的守護(hù)進(jìn)程,在守護(hù)進(jìn)程中執(zhí)行軟中斷的回調(diào)函數(shù)。否則什么也不做,軟中斷將會(huì)在中斷的退出階段被執(zhí)行。


3.  軟中斷的執(zhí)行


        基于上面所說(shuō),軟中斷的執(zhí)行既可以守護(hù)進(jìn)程中執(zhí)行,也可以在中斷的退出階段執(zhí)行。實(shí)際上,軟中斷更多的是在中斷的退出階段執(zhí)行(irq_exit),以便達(dá)到更快的響應(yīng),加入守護(hù)進(jìn)程機(jī)制,只是擔(dān)心一旦有大量的軟中斷等待執(zhí)行,會(huì)使得內(nèi)核過(guò)長(zhǎng)地留在中斷上下文中。


3.1  在irq_exit中執(zhí)行

        看看irq_exit的部分:
  1. void irq_exit(void)  
  2. {  
  3.         ......  
  4.     sub_preempt_count(IRQ_EXIT_OFFSET);  
  5.     if (!in_interrupt() && local_softirq_pending())  
  6.         invoke_softirq();  
  7.         ......  
  8. }  
如果中斷發(fā)生嵌套,in_interrupt()保證了只有在最外層的中斷的irq_exit階段,invoke_interrupt才會(huì)被調(diào)用,當(dāng)然,local_softirq_pending也會(huì)實(shí)現(xiàn)判斷當(dāng)前cpu有無(wú)待決的軟中斷。代碼最終會(huì)進(jìn)入__do_softirq中,內(nèi)核會(huì)保證調(diào)用__do_softirq時(shí),本地cpu的中斷處于關(guān)閉狀態(tài),進(jìn)入__do_softirq:
  1. asmlinkage void __do_softirq(void)  
  2. {  
  3.         ......  
  4.     pending = local_softirq_pending();  
  5.   
  6.     __local_bh_disable((unsigned long)__builtin_return_address(0),  
  7.                 SOFTIRQ_OFFSET);  
  8. restart:  
  9.     /* Reset the pending bitmask before enabling irqs */  
  10.     set_softirq_pending(0);  
  11.   
  12.     local_irq_enable();  
  13.   
  14.     h = softirq_vec;  
  15.   
  16.     do {  
  17.         if (pending & 1) {  
  18.                     ......  
  19.             trace_softirq_entry(vec_nr);  
  20.             h->action(h);  
  21.             trace_softirq_exit(vec_nr);  
  22.                         ......  
  23.         }  
  24.         h++;  
  25.         pending >>= 1;  
  26.     } while (pending);  
  27.   
  28.     local_irq_disable();  
  29.   
  30.     pending = local_softirq_pending();  
  31.     if (pending && --max_restart)  
  32.         goto restart;  
  33.   
  34.     if (pending)  
  35.         wakeup_softirqd();  
  36.   
  37.     lockdep_softirq_exit();  
  38.   
  39.     __local_bh_enable(SOFTIRQ_OFFSET);  
  40. }  
  • 首先取出pending的狀態(tài);
  • 禁止軟中斷,主要是為了防止和軟中斷守護(hù)進(jìn)程發(fā)生競(jìng)爭(zhēng);
  • 清除所有的軟中斷待決標(biāo)志;
  • 打開(kāi)本地cpu中斷;
  • 循環(huán)執(zhí)行待決軟中斷的回調(diào)函數(shù);
  • 如果循環(huán)完畢,發(fā)現(xiàn)新的軟中斷被觸發(fā),則重新啟動(dòng)循環(huán),直到以下條件滿足,才退出:
    • 沒(méi)有新的軟中斷等待執(zhí)行;
    • 循環(huán)已經(jīng)達(dá)到最大的循環(huán)次數(shù)MAX_SOFTIRQ_RESTART,目前的設(shè)定值時(shí)10次;
  • 如果經(jīng)過(guò)MAX_SOFTIRQ_RESTART次循環(huán)后還未處理完,則激活守護(hù)進(jìn)程,處理剩下的軟中斷;
  • 推出前恢復(fù)軟中斷;


3.2  在ksoftirqd進(jìn)程中執(zhí)行

        從前面幾節(jié)的討論我們可以看出,軟中斷也可能由ksoftirqd守護(hù)進(jìn)程執(zhí)行,這要發(fā)生在以下兩種情況下:
  • 在irq_exit中執(zhí)行軟中斷,但是在經(jīng)過(guò)MAX_SOFTIRQ_RESTART次循環(huán)后,軟中斷還未處理完,這種情況雖然極少發(fā)生,但畢竟有可能;
  • 內(nèi)核的其它代碼主動(dòng)調(diào)用raise_softirq,而這時(shí)正好不是在中斷上下文中,守護(hù)進(jìn)程將被喚醒;
守護(hù)進(jìn)程最終也會(huì)調(diào)用__do_softirq執(zhí)行軟中斷的回調(diào),具體的代碼位于run_ksoftirqd函數(shù)中,內(nèi)核會(huì)關(guān)閉搶占的情況下執(zhí)行__do_softirq,具體的過(guò)程這里不做討論。


4.  tasklet


       因?yàn)閮?nèi)核已經(jīng)定義好了10種軟中斷類型,并且不建議我們自行添加額外的軟中斷,所以對(duì)軟中斷的實(shí)現(xiàn)方式,我們主要是做一個(gè)簡(jiǎn)單的了解,對(duì)于驅(qū)動(dòng)程序的開(kāi)發(fā)者來(lái)說(shuō),無(wú)需實(shí)現(xiàn)自己的軟中斷。但是,對(duì)于某些情況下,我們不希望一些操作直接在中斷的handler中執(zhí)行,但是又希望在稍后的時(shí)間里得到快速地處理,這就需要使用tasklet機(jī)制。 tasklet是建立在軟中斷上的一種延遲執(zhí)行機(jī)制,它的實(shí)現(xiàn)基于TASKLET_SOFTIRQ和HI_SOFTIRQ這兩個(gè)軟中斷類型。


4.1  tasklet_struct        


在軟中斷的初始化函數(shù)softirq_init的最后,內(nèi)核注冊(cè)了TASKLET_SOFTIRQ和HI_SOFTIRQ這兩個(gè)軟中斷:
  1. void __init softirq_init(void)  
  2. {  
  3.         ......  
  4.     open_softirq(TASKLET_SOFTIRQ, tasklet_action);  
  5.     open_softirq(HI_SOFTIRQ, tasklet_hi_action);  
  6. }  
        內(nèi)核用一個(gè)tasklet_struct來(lái)表示一個(gè)tasklet,它的定義如下:
  1. struct tasklet_struct  
  2. {  
  3.     struct tasklet_struct *next;  
  4.     unsigned long state;  
  5.     atomic_t count;  
  6.     void (*func)(unsigned long);  
  7.     unsigned long data;  
  8. };  
next用于把同一個(gè)cpu的tasklet鏈接成一個(gè)鏈表,state用于表示該tasklet的當(dāng)前狀態(tài),目前只是用了最低的兩個(gè)bit,分別用于表示已經(jīng)準(zhǔn)備被調(diào)度執(zhí)行和已經(jīng)在另一個(gè)cpu上執(zhí)行:
  1. enum  
  2. {  
  3.     TASKLET_STATE_SCHED,    /* Tasklet is scheduled for execution */  
  4.     TASKLET_STATE_RUN   /* Tasklet is running (SMP only) */  
  5. };  
原子變量count用于tasklet對(duì)tasklet_disable和tasklet_enable的計(jì)數(shù),count為0時(shí)表示允許tasklet執(zhí)行,否則不允許執(zhí)行,每次tasklet_disable時(shí),該值加1,tasklet_enable時(shí)該值減1。func是tasklet被執(zhí)行時(shí)的回調(diào)函數(shù)指針,data則用作回調(diào)函數(shù)func的參數(shù)。


4.2  初始化一個(gè)tasklet


有兩種辦法初始化一個(gè)tasklet,第一種是靜態(tài)初始化,使用以下兩個(gè)宏,這兩個(gè)宏定義一個(gè)tasklet_struct結(jié)構(gòu),并用相應(yīng)的參數(shù)對(duì)結(jié)構(gòu)中的字段進(jìn)行初始化:
  • DECLARE_TASKLET(name, func, data);定義名字為name的tasklet,默認(rèn)為enable狀態(tài),也就是count字段等于0。
  • DECLARE_TASKLET_DISABLED(name, func, data);定義名字為name的tasklet,默認(rèn)為enable狀態(tài),也就是count字段等于1。
第二個(gè)是動(dòng)態(tài)初始化方法:先定義一個(gè)tasklet_struct,然后用tasklet_init函數(shù)進(jìn)行初始化,該方法默認(rèn)tasklet處于enable狀態(tài):
  1. struct tasklet_struct tasklet_xxx;  
  2. ......  
  3. tasklet_init(&tasklet_xxx, func, data);  


4.3  tasklet的使用方法


使能和禁止tasklet,使用以下函數(shù):
  • tasklet_disable()  通過(guò)給count字段加1來(lái)禁止一個(gè)tasklet,如果tasklet正在運(yùn)行中,則等待運(yùn)行完畢才返回(通過(guò)TASKLET_STATE_RUN標(biāo)志)。
  • tasklet_disable_nosync()  tasklet_disable的異步版本,它不會(huì)等待tasklet運(yùn)行完畢。
  • tasklet_enable()  使能tasklet,只是簡(jiǎn)單地給count字段減1。
調(diào)度tasklet的執(zhí)行,使用以下函數(shù):
  • tasklet_schedule(struct tasklet_struct *t)  如果TASKLET_STATE_SCHED標(biāo)志為0,則置位TASKLET_STATE_SCHED,然后把tasklet掛到該cpu等待執(zhí)行的tasklet鏈表上,接著發(fā)出TASKLET_SOFTIRQ軟件中斷請(qǐng)求。
  • tasklet_hi_schedule(struct tasklet_struct *t)  效果同上,區(qū)別是它發(fā)出的是HI_SOFTIRQ軟件中斷請(qǐng)求。
銷毀tasklet,使用以下函數(shù):
  • tasklet_kill(struct tasklet_struct *t)  如果tasklet處于TASKLET_STATE_SCHED狀態(tài),或者tasklet正在執(zhí)行,則會(huì)等待tasklet執(zhí)行完畢,然后清除TASKLET_STATE_SCHED狀態(tài)。


4.4  tasklet的內(nèi)部執(zhí)行機(jī)制


內(nèi)核為每個(gè)cpu用定義了一個(gè)tasklet_head結(jié)構(gòu),用于管理每個(gè)cpu上的tasklet的調(diào)度和執(zhí)行:
  1. struct tasklet_head  
  2. {  
  3.     struct tasklet_struct *head;  
  4.     struct tasklet_struct **tail;  
  5. };  
  6.   
  7. static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec);  
  8. static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec);  
回到4.1節(jié),我們知道,tasklet是利用TASKLET_SOFTIRQ和HI_SOFTIRQ這兩個(gè)軟中斷來(lái)實(shí)現(xiàn)的,兩個(gè)軟中斷只是有優(yōu)先級(jí)的差別,所以我們只討論TASKLET_SOFTIRQ的實(shí)現(xiàn),TASKLET_SOFTIRQ的中斷回調(diào)函數(shù)是tasklet_action,我們看看它的代碼:
  1. static void tasklet_action(struct softirq_action *a)  
  2. {  
  3.     struct tasklet_struct *list;  
  4.   
  5.     local_irq_disable();  
  6.     list = __this_cpu_read(tasklet_vec.head);  
  7.     __this_cpu_write(tasklet_vec.head, NULL);  
  8.     __this_cpu_write(tasklet_vec.tail, &__get_cpu_var(tasklet_vec).head);  
  9.     local_irq_enable();  
  10.   
  11.     while (list) {  
  12.         struct tasklet_struct *t = list;  
  13.   
  14.         list = list->next;  
  15.   
  16.         if (tasklet_trylock(t)) {  
  17.             if (!atomic_read(&t->count)) {  
  18.                 if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))  
  19.                     BUG();  
  20.                 t->func(t->data);  
  21.                 tasklet_unlock(t);  
  22.                 continue;  
  23.             }  
  24.             tasklet_unlock(t);  
  25.         }  
  26.   
  27.         local_irq_disable();  
  28.         t->next = NULL;  
  29.         *__this_cpu_read(tasklet_vec.tail) = t;  
  30.         __this_cpu_write(tasklet_vec.tail, &(t->next));  
  31.         __raise_softirq_irqoff(TASKLET_SOFTIRQ);  
  32.         local_irq_enable();  
  33.     }  
  34. }  
解析如下:
  • 關(guān)閉本地中斷的前提下,移出當(dāng)前cpu的待處理tasklet鏈表到一個(gè)臨時(shí)鏈表后,清除當(dāng)前cpu的tasklet鏈表,之所以這樣處理,是為了處理當(dāng)前tasklet鏈表的時(shí)候,允許新的tasklet被調(diào)度進(jìn)待處理鏈表中。
  • 遍歷臨時(shí)鏈表,用tasklet_trylock判斷當(dāng)前tasklet是否已經(jīng)在其他cpu上運(yùn)行,而且tasklet沒(méi)有被禁止:
    • 如果沒(méi)有運(yùn)行,也沒(méi)有禁止,則清除TASKLET_STATE_SCHED狀態(tài)位,執(zhí)行tasklet的回調(diào)函數(shù)。
    • 如果已經(jīng)在運(yùn)行,或者被禁止,則把該tasklet重新添加會(huì)當(dāng)前cpu的待處理tasklet鏈表上,然后觸發(fā)TASKLET_SOFTIRQ軟中斷,等待下一次軟中斷時(shí)再次執(zhí)行。
分析到這了我有個(gè)疑問(wèn),看了上面的代碼,如果一個(gè)tasklet被tasklet_schedule后,在沒(méi)有被執(zhí)行前被tasklet_disable了,豈不是會(huì)無(wú)窮無(wú)盡地引發(fā)TASKLET_SOFTIRQ軟中斷?
通過(guò)以上的分析,我們需要注意的是,tasklet有以下幾個(gè)特征:
  • 同一個(gè)tasklet只能同時(shí)在一個(gè)cpu上執(zhí)行,但不同的tasklet可以同時(shí)在不同的cpu上執(zhí)行;
  • 一旦tasklet_schedule被調(diào)用,內(nèi)核會(huì)保證tasklet一定會(huì)在某個(gè)cpu上執(zhí)行一次;
  • 如果tasklet_schedule被調(diào)用時(shí),tasklet不是出于正在執(zhí)行狀態(tài),則它只會(huì)執(zhí)行一次;
  • 如果tasklet_schedule被調(diào)用時(shí),tasklet已經(jīng)正在執(zhí)行,則它會(huì)在稍后被調(diào)度再次被執(zhí)行;
  • 兩個(gè)tasklet之間如果有資源沖突,應(yīng)該要用自旋鎖進(jìn)行同步保護(hù);

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開(kāi)APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
中斷下半部分析(tasklet)
tasklet_init用法
linux內(nèi)核中斷-----tasklet分析
十四、Linux驅(qū)動(dòng)程序開(kāi)發(fā)(10) - 中斷
Linux內(nèi)核中常見(jiàn)內(nèi)存分配函數(shù)
linux 中斷機(jī)制淺析
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服