欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

一步步理解Linux之中斷和異常

系統(tǒng) 2270 0

一步步理解Linux之中斷和異常

作者: gaopenghigh ?,轉(zhuǎn)載請注明出處。? (原文地址)


中斷和異常的概念

*? 中斷 : 硬件通過中斷來通知內(nèi)核。中斷是一種電信號,由硬件設(shè)備生成,并送入中斷控制器 的輸入引腳中,中斷控制器會(huì)給CPU發(fā)送一個(gè)電信號,CPU檢測到這個(gè)信號,就中斷當(dāng) 前的工作轉(zhuǎn)而處理中斷。每個(gè)中斷都通過一個(gè)唯一的數(shù)字標(biāo)志。這些中斷值稱為? 中斷請求(IRQ,Interrupt ReQuest)線 。

*? 異常 : 當(dāng)CPU執(zhí)行到由于編程失誤而導(dǎo)致的錯(cuò)誤指令(比如被0除)的時(shí)候,或者在執(zhí)行期間 出現(xiàn)踢輸情況(如缺葉)而必須靠內(nèi)核來處理的時(shí)候,處理器就產(chǎn)生一個(gè)異常。異常 和中斷類似,所以異常也叫“同步中斷(asynchronous interrupt)”。內(nèi)核對異常的處 理大部分和對中斷的處理一樣。

中斷描述符表

中斷描述符表(Interrupt Descriptor Table, IDT)是一個(gè)系統(tǒng)表,它與每一個(gè)中斷或異 常向量相聯(lián)系,每一個(gè)向量在表中有相應(yīng)的中斷或異常處理程序的入口地址。IDT的地址存 放在 idtr 寄存器中。中斷發(fā)生時(shí),內(nèi)核就從IDT中查詢相應(yīng)中斷的處理信息。

異常處理

異常處理一般由三個(gè)部分組成:

1. 在內(nèi)核堆棧中保存大多數(shù)寄存器的內(nèi)容(匯編)。

  1. 用高級的C函數(shù)處理異常。
  2. 通過 ret_from_exception() 函數(shù)從異常處理程序退出。

中斷處理

中斷處理一般由四個(gè)步驟組成:

  1. 在內(nèi)核態(tài)堆棧中保存IRQ的值和寄存器的內(nèi)容。
  2. 為正在給IRQ線服務(wù)的PIC發(fā)送一個(gè)應(yīng)答,這將允許PIC進(jìn)一步發(fā)出中斷。
  3. 執(zhí)行共享這個(gè)IRQ的所有設(shè)備的中斷服務(wù)例程(ISR)。
  4. 跳到 ret_from_intr() 的地址。

中斷處理的示意圖如下:

中斷處理程序

在相應(yīng)一個(gè)特定中斷時(shí),內(nèi)核會(huì)執(zhí)行一個(gè)函數(shù),這個(gè)函數(shù)就叫做? 中斷處理程序(interrupt handler) ,或者叫做? 中斷服務(wù)例程(interrupt service routine,ISR)

中斷處理程序運(yùn)行在中斷上下文中,該上下文中的代碼不可以阻塞。要注意,中斷處理程 序執(zhí)行的代碼不是一個(gè)進(jìn)程,中斷處理程序比一個(gè)進(jìn)程要“輕”。

每個(gè)中斷和異常都會(huì)引起一個(gè)內(nèi)核控制路徑,而內(nèi)核控制路徑是可以任意嵌套的。也就是 說,一個(gè)中斷處理程序可以被另一個(gè)中斷處理程序“中斷”。為了允許這樣的嵌套,中斷處 理程序就必須永不阻塞,換句話說,進(jìn)程被中斷,在中斷程序運(yùn)行期間,不能發(fā)生進(jìn)程切 換。這是因?yàn)?,一個(gè)中斷產(chǎn)生時(shí),內(nèi)核會(huì)把當(dāng)前寄存器的內(nèi)容保存在內(nèi)核態(tài)堆棧中,這個(gè) 內(nèi)核態(tài)堆棧屬于當(dāng)前進(jìn)程,嵌套中斷時(shí),上一個(gè)中斷執(zhí)行程序產(chǎn)生的寄存器內(nèi)容同樣也會(huì) 保存在該內(nèi)核態(tài)堆棧,然后從嵌套的下一個(gè)中斷恢復(fù)時(shí),又從內(nèi)核態(tài)堆棧中取出來放進(jìn)寄 存器中。

一個(gè)內(nèi)核控制路徑嵌套執(zhí)行的示例圖如下:

Linux中中斷處理程序是無須重入的。當(dāng)一條中斷線上的handler正在執(zhí)行時(shí),這條中斷線 在所有處理器上都會(huì)被屏蔽掉。

在/proc/interrupts中可以查看當(dāng)前系統(tǒng)的中斷統(tǒng)計(jì)信息。

IRQ數(shù)據(jù)結(jié)構(gòu)

每個(gè)IRQ都有自己的描述符 irq_desc_t ,描述符中有字段指向PIC對象,有字段指向ISR的 鏈表(因?yàn)槊總€(gè)IRQ線上可以注冊多個(gè)中斷處理程序)。所有的 irq_desc_t 合起來組成? irq_desc 數(shù)組。示例圖如下:

上半部和下半部的概念

有時(shí)候中斷處理需要做的工作很多,而中斷處理程序的性質(zhì)要求它必須在盡量短的時(shí) 間內(nèi)處理完畢,所以中斷處理的過程可以分為兩部分或者兩半(half)。中斷處理程序?qū)?于“上半部(top half)”–接受到一個(gè)中斷,立刻開始執(zhí)行,但只做有嚴(yán)格時(shí)限的工作。 能夠被允許稍微晚一點(diǎn)完成的工作會(huì)放到“下半部(bottom half)中去,下半部不會(huì)馬上 執(zhí)行,而是等到一個(gè)合適的時(shí)機(jī)調(diào)度執(zhí)行。也就是說,關(guān)鍵而緊急的部分,內(nèi)核立即執(zhí)行 ,屬于上半部;其余推遲的部分,內(nèi)核隨后執(zhí)行,屬于下半部。

比如說當(dāng)網(wǎng)卡接收到數(shù)據(jù)包時(shí),會(huì)產(chǎn)生一個(gè)中斷,中斷處理程序首要進(jìn)行的工作是通知硬 件拷貝最新的網(wǎng)絡(luò)數(shù)據(jù)包到內(nèi)存,然后讀取網(wǎng)卡更多的數(shù)據(jù)包。這樣網(wǎng)卡緩存就不會(huì)溢出 。至于對數(shù)據(jù)包的處理和其他隨后工作,則放到下半部進(jìn)行。關(guān)于下半部的細(xì)節(jié),我們后 面會(huì)討論。

注冊中斷處理程序

驅(qū)動(dòng)程序通過 request_irq() 函數(shù)注冊一個(gè)中斷處理程序:

    
      /* 定義在<linux/interrupt.h>中 */

typedef irqreturn_t (*irq_handler_t)(int, void *);



int request_irq(ussigned int irq,

                irq_handler_t handler,

                unsigned long flags,

                const char *name,

                void *dev);
    
  

參數(shù)解釋如下:

  • irq ?要分配的中斷號

  • handler ?是指向中斷處理程序的指針

  • flags ?設(shè)置中斷處理程序的一些屬性,可能的值如下:

            
                IRQF_DISABLED       在本次中斷處理程序本身期間,禁止所有其他中斷。
    
      IRQF_SAMPLE_RANDOM  這個(gè)中斷對內(nèi)核的隨機(jī)數(shù)產(chǎn)生源有貢獻(xiàn)。
    
      IRQF_TIMER          該標(biāo)志是特別為系統(tǒng)定時(shí)器的中斷處理準(zhǔn)備的。
    
      IRQF_SHARED         表明多個(gè)中斷處理程序可以共享這條中斷線。也就是說這
    
                          條中斷線上可以注冊多個(gè)中斷處理程序,當(dāng)中斷發(fā)生時(shí),
    
                          所有注冊到這條中斷線上的handler都會(huì)被調(diào)用。
            
          
  • name ?是與中斷相關(guān)設(shè)備的ASCII文本表示

  • dev ?類似于一個(gè)cookie,內(nèi)核每次調(diào)用中斷處理程序時(shí),都會(huì)把這個(gè)指針傳遞給它, 指針的值用來表明到底是什么設(shè)備產(chǎn)生了這個(gè)中斷,當(dāng)中斷線共享時(shí),這條中斷線上 的handler們就可以通過dev來判斷自己是否需要處理。

釋放中斷處理程序

通過 free_irq 函數(shù)注銷相應(yīng)的中斷處理程序:

    
      void free_irq(unsigned int irq, void *dev);
    
  

參數(shù)和 request_irq 的參數(shù)類似。當(dāng)一條中斷線上注冊了多個(gè)中斷處理程序時(shí),就需要? dev 來說明想要注銷的是哪一個(gè)handler。


下半部(bottom half)

有三種機(jī)制來執(zhí)行下半部的工作:“軟中斷”,“tasklet”和“工作隊(duì)列”。

軟中斷是一組靜態(tài)定義的下半部接口,有32個(gè),可以在所有處理器上同時(shí)執(zhí)行–即使兩個(gè) 類型相同也可以。

tasklet的實(shí)現(xiàn)基于軟中斷,但兩個(gè)相同類型的tasklet不能同時(shí)執(zhí)行。

工作隊(duì)列則是先對要推后執(zhí)行的工作排隊(duì),稍后在進(jìn)程上下文中執(zhí)行它們。

軟中斷(softirq)

軟中斷的實(shí)現(xiàn)

軟中斷實(shí)在編譯期間靜態(tài)分配的,由 softirq_action 結(jié)構(gòu)表示:

    
      /* 在<linux/interrupt.h>中 */

struct softirq_action {

    void (*action)(struct softirq_action *);

};

/* kernel/softirq.c中定義了一個(gè)包含有32個(gè)該結(jié)構(gòu)體的數(shù)組 */

static struct softirq_action softirq_vec[NR_SOFTIRQS];
    
  

每個(gè)被注冊的軟中斷都占據(jù)該數(shù)組的一項(xiàng),因此最多可能有32個(gè)軟中斷。

當(dāng)內(nèi)核運(yùn)行一個(gè)軟中斷處理程序的時(shí)候,就會(huì)執(zhí)行 softirq_action 結(jié)構(gòu)中的 action 指 向的函數(shù):

    
      my_softirq->action(my_softirq);
    
  

它把自己(整個(gè) softirq_action 結(jié)構(gòu))的指針作為參數(shù)。

軟中斷的觸發(fā)

軟中斷在被標(biāo)記后才會(huì)執(zhí)行,這標(biāo)記的過程叫做 觸發(fā)軟中斷(raising the softirq) ?。通常在中斷處理程序中觸發(fā)軟中斷。軟中斷的觸發(fā)通過 raise_softirq() 進(jìn)行。比如

    
      raise_softirq(NET_TX_SOFTIRQ);
    
  

觸發(fā)網(wǎng)絡(luò)子系統(tǒng)的軟中斷。

在下面這些時(shí)刻,軟中斷會(huì)被檢查和執(zhí)行:

* 從一個(gè)硬件中斷代碼處返回時(shí) * 在ksoftirqd內(nèi)核線程中(稍后會(huì)講到) * 在那些顯式檢查和執(zhí)行帶處理的軟中斷的代碼中,比如網(wǎng)絡(luò)子系統(tǒng)中

軟中斷的執(zhí)行

軟中斷的狀態(tài)通過一個(gè)位圖來表示:第n位設(shè)置為1,表示第n個(gè)類型的軟中斷被觸發(fā),等待 處理。 local_softirq_pending() 宏返回這個(gè)位圖。 set_softirq_pending() 宏則可對 位圖進(jìn)行設(shè)置或清零。

軟中斷在 do_softirq() 函數(shù)中執(zhí)行,該函數(shù)遍歷每一個(gè)軟中斷,如果處于被觸發(fā)的狀態(tài) ,則執(zhí)行其處理程序,該函數(shù)的核心部分類似與這樣:

    
      u32 pending;

pending = local_softirq_pending();



if (pending) {

    struct softirq_action *h;

    set_softirq_pending(0);           /* 把位圖清零 */



    h = soft_vec;

    do {

        if (pending & 1)

            h-action(h);

        h++;

        pending >>= 1;      /* 位圖向右移1位,原來第二位的現(xiàn)在在第一位 */

    } while (pending);

}
    
  

需要注意的是,如果同一個(gè)軟中斷在它被執(zhí)行的同時(shí)又被觸發(fā)了,那么另外一個(gè)處理器可 以同時(shí)運(yùn)行其處理程序。這意味著任何共享數(shù)據(jù)(甚至是僅在軟中斷處理程序內(nèi)部使用的 全局變量)都需要嚴(yán)格的鎖保護(hù)。因此,大部分的軟中斷處理程序,都通過采取單處理器 數(shù)據(jù)或其他的一些技巧來避免顯式地加鎖。

tasklet

tasklet的實(shí)現(xiàn)

tasklet基于軟中斷實(shí)現(xiàn),事實(shí)上它使用的是 HI_SOFTIRQ TASKLET_SOFTIRQ 這兩個(gè)軟 中斷,通過 tasklet_struct 結(jié)構(gòu)表示:

    
      /* 在<linux/interrupt.h>中 */

struct tasklet_struct {

    struct tasklet_struct *next;       /* 鏈表中的下一個(gè)tasklet */

    unsigned long state;               /* tasklet的狀態(tài) */

    atomic_t count;                    /* 引用計(jì)數(shù)器 */

    void (*func)(unsigned long);       /* tasklet處理函數(shù) */

    unsigned long data;                /* 給tasklet處理函數(shù)的參數(shù) */

};
    
  

其中,state的值只可以為0, TASKLET_STATE_SCHED (表示tasklet已被調(diào)度,正在準(zhǔn)備 投入運(yùn)行),和 TASKLET_STATE_RUN (表示tasklet正在運(yùn)行)。

tasklet的調(diào)度

已經(jīng)調(diào)度的tasklet(相當(dāng)于觸發(fā)了的軟中斷)存放在兩個(gè)由 tasklet_struct 結(jié)構(gòu)組成的 鏈表中: tasklet_vec tasklet_hi_vec (表示高優(yōu)先級的tasklet),分別通過 tasklet_schedule() tasklet_hi_schedule() 進(jìn)行調(diào)度。

ksoftirqd

在軟中斷處理程序中有時(shí)候會(huì)再次觸發(fā)軟中斷,這樣就有可能出現(xiàn)大量的軟中斷。這些重 新觸發(fā)的軟中斷不會(huì)馬上被處理,而是通過內(nèi)核喚醒的一組內(nèi)核線程來處理的。

每個(gè)處理器都有一組輔助處理軟中斷(包括了tasklet)的內(nèi)核線程,名字叫做? ksoftirqd/n ,其中n代表CPU的編號。這些內(nèi)核線程以最低的優(yōu)先級運(yùn)行(nice值19), 這樣就能避免它們和其它重要的任務(wù)搶奪資源。這些內(nèi)核線程會(huì)執(zhí)行類似與下面的循環(huán):

    
      for (;;) {

    if (!softirq_pending(cpu))

        schedule();

    set_current_state(TASK_RUNNING);

    while (softirq_pending(cpu)) {

        do_softirq();

        if (need_resched())

            shcedule();

    }

    set_current_state(TASK_INTERRUPTIBLE);

}
    
  

preempt_count字段

在每個(gè)進(jìn)程描述符的 thread_info 結(jié)構(gòu)中有一個(gè)32位的字段叫 preempt_count ,它用來 跟蹤內(nèi)核搶占和內(nèi)核控制路徑的嵌套。利用 preempt_count 的不同區(qū)域表示不同的計(jì)數(shù)器 和一個(gè)標(biāo)志。

    
      位        描述

0~7       搶占計(jì)數(shù)器(max value = 255)

8~15      軟中斷計(jì)數(shù)器(max value = 255)

16~27     硬中斷計(jì)數(shù)器(max value = 4096)

28        PREEMPT_ACTIVE 標(biāo)志
    
  
  • “搶占計(jì)數(shù)器”記錄顯式禁用本地CPU內(nèi)核搶占的次數(shù),只有當(dāng)這個(gè)計(jì)數(shù)器為0時(shí)才允許內(nèi) 核搶占。
  • “軟中斷計(jì)數(shù)器”表示軟中斷被禁用的程度,同樣,值為0時(shí)表示軟中斷可以被觸發(fā)。
  • “硬中斷計(jì)數(shù)器”表示本地CPU上中斷處理程序的嵌套數(shù)。 irq_enter() 宏遞增它的值,? irq_exit() 宏遞減它的值。

工作隊(duì)列

工作隊(duì)列(work queue) 是另外一種將工作推后執(zhí)行的形式,它可以把工作推后,交 由一個(gè)內(nèi)核線程去執(zhí)行。所以這些工作會(huì)在進(jìn)程上下文中執(zhí)行,并且運(yùn)行重新調(diào)度和睡眠 。

工作的表示

一個(gè)工作用 work_struct 結(jié)構(gòu)體表示:

    
      /* 定義在<linux/workqueue.h>中 */

typedef void (*work_func_t)(struct work_struct *work);

struct work_struct {

    atomic_long_t data;      /* 執(zhí)行這個(gè)工作時(shí)的參數(shù) */

    struct list_head entry;  /* 工作組成的鏈表 */

    work_func_t func;        /* 執(zhí)行這個(gè)工作時(shí)調(diào)用的函數(shù) */

};
    
  

這些 work_struct 構(gòu)成一個(gè)鏈表,工作執(zhí)行完畢時(shí),該工作就會(huì)從鏈表中移除。

工作者線程的表示

可以把一些工作放到一個(gè)隊(duì)列里面,然后創(chuàng)建一個(gè)專門的內(nèi)核線程來執(zhí)行隊(duì)列里的任務(wù), 這些內(nèi)核線程叫做 工作者線程(worker thread) 。但是大多數(shù)情況下不需要自己創(chuàng)建 worker thread,因?yàn)閮?nèi)核已經(jīng)創(chuàng)建了一個(gè)默認(rèn)的,叫做 events/n ,這里的n表示CPU的編 號。

“worker thread”使用 workqueue_struct 結(jié)構(gòu)表示:

    
      struct workqueue_struct {

    struct cpu_workqueue_struct cpu_wq[NR_CPUS];

    struct list_head list;

    const char *name;

    int singlethread;

    int freezeable;

    int rt;

};
    
  

一個(gè)“worker thread”表示一種類型的工作者線程,默認(rèn)情況下只有event這一種類型的工 作者線程。然后每一個(gè)CPU上又有一個(gè)該類型的工作者線程,這就表現(xiàn)為 cpu_wq 數(shù)組,該 數(shù)組的每一項(xiàng)是 struct cpu_workqueue_struct 結(jié)構(gòu):

    
      struct cpu_workqueue_struct {

    spinlock_t lock;               /* 通過自旋鎖保護(hù)該結(jié)構(gòu) */

    struct list_head worklist;     /* 工作列表 */

    wait_queue_head_t more_work;

    struct work_struct *current_struct;

    struct workqueue_struct *wq;   /* 關(guān)聯(lián)工作隊(duì)列結(jié)構(gòu) */

    task_t *thread;                /* 關(guān)聯(lián)線程 */

};
    
  

該結(jié)構(gòu)體中的 wq 表明自己是什么類型的worker。


系統(tǒng)調(diào)用

什么是系統(tǒng)調(diào)用

系統(tǒng)調(diào)用(System Call) 就是讓用戶進(jìn)程與內(nèi)核進(jìn)行交互的一組接口,它在用戶進(jìn)程 和硬件設(shè)備之間添加了一個(gè)中間層。

printf() 為例,應(yīng)用程序、C庫和內(nèi)核之間的關(guān)系是:

    
      -------------------------------------------------------------------------

printf() ----> C庫中的printf() ----> C庫中的write() ----> write()系統(tǒng)調(diào)用

--------------------------------------------------------------------------

| 應(yīng)用程序 |                   C庫                    |      內(nèi)核        |

--------------------------------------------------------------------------
    
  

每個(gè)系統(tǒng)調(diào)用被賦予一個(gè)獨(dú)一無二的系統(tǒng)調(diào)用號,系統(tǒng)調(diào)用號一旦分配就不能再變更。否 則編譯好的程序就會(huì)崩潰。

系統(tǒng)調(diào)用處理程序 system_call()

應(yīng)用程序是通過 軟中斷 來通知內(nèi)核對系統(tǒng)調(diào)用的進(jìn)行使用的, 事實(shí)上是第128號IRQ。 也就是通過引發(fā)一個(gè)異常來促使系統(tǒng)切換到內(nèi)核態(tài)去執(zhí)行異常處理程序。此時(shí)的異常處理 程序?qū)嶋H上就是系統(tǒng)調(diào)用處理程序– system_call()

至于使用的是哪個(gè)系統(tǒng)調(diào)用,就是通過系統(tǒng)調(diào)用號來判斷。在陷入內(nèi)核空間前,用戶空間 把相應(yīng)的系統(tǒng)調(diào)用號存入 exa 寄存器, system_call 通過 exa 寄存器得知到底是哪個(gè)系 統(tǒng)調(diào)用。參數(shù)的傳遞也是通過寄存器,如果參數(shù)較多,則寄存器里面存的是指向這些參數(shù) 的用戶空間地址的指針。

JH, 2013-05-05


參考資料:

一步步理解Linux之中斷和異常


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會(huì)非常 感謝您的哦?。?!

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 久久久久久久久久综合 | 国产传媒在线视频 | 欧美一级视频在线观看欧美 | 日日舔夜夜操 | 国产精品成人在线观看 | 亚洲一区2区三区4区5区 | 精品在线视频一区 | 2021国产精品 | 一国产一级淫片a免费播放口 | 一区二区av | 亚洲码在线 | 亚洲欧美影视 | xxx视频 | 日韩精选在线 | 欧美成人私人视频88在线观看 | 国产成人免费高清激情明星 | 91短视频免费版 | 色综合97天天综合网 | 欧美亚洲视频在线观看 | 国产丫丫视频私人影院 | 免费视频片在线观看大片 | jjizz老女人多水喷水 | 成人免费毛片高清视频 | 久久精品国产免费看久久精品 | 日日拍夜夜嗷嗷叫视频 | 黄色免费在线观看网址 | 91网站入口 | 国产chinese视频在线观看 | 综合一区二区三区 | 成人影院欧美大片免费看 | 天天摸天天做天天爽 | 国产精品a久久久久 | 亚洲精品久久久久久一区 | 逼逼网| www.yw193.com | 亚洲综合视频在线观看 | 日韩综合在线 | 国产精品第一国产精品 | 天天影院成人免费观看 | 成人在线精品 | 一级毛片一 |