【嵌入式Linux學習七步曲之第五篇 Linux內核及驅動編程】深入剖析Linux中斷機制之四--中斷API

轉載:http://blog.csdn.net/sailor_8318/archive/2008/07/09/2627136.aspx

深入剖析Linux中斷機制之四

--中斷API

【摘要】本文詳解了Linux內核的中斷實現機制。首先介紹了中斷的一些基本概念,然後分析了面向對象的Linux中斷的組織形式、三種主要數據結構及其之間的關係。隨後介紹了Linux處理異常和中斷的基本流程,在此基礎上分析了中斷處理的詳細流程,包括保存現場、中斷處理、中斷退出時的軟中斷執行及中斷返回時的進程切換等問題。最後介紹了中斷相關的API,包括中斷註冊和釋放、中斷關閉和使能、如何編寫中斷ISR、共享中斷、中斷上下文中斷狀態等。

【關鍵字】中斷,異常,hw_interrupt_type,irq_desc_t,irqaction,asm_do_IRQ,軟中斷,進程切換,中斷註冊釋放request_irq,free_irq,共享中斷,可重入,中斷上下文

1       中斷API

內核提供的中斷接口包括註冊和註銷中斷處理程序,禁止中斷,屏蔽中斷線,以及檢查中斷系統的狀態。

1.1.1      中斷服務例程的掛接

在設備驅動程序的初始化階段,必須通過request_irq()函數將對相應的中斷服務例程掛入中斷請求隊列,註冊並激活一箇中斷處理程序,以便處理中斷:

int request_irq(unsigned int irq, irq_handler_thandler,unsigned long irqflags, const char *devname, void *dev_id)

²      unsigned int irq

要分配的中斷號,通常是預先設計固定的。某些情況可以通過探測獲得,如PCI

²      irqreturn_t (*handler)

一個指針,指向處理這個中斷的實際中斷處理程序。需要符合中斷處理函數的原型,2.4及2.6的早期版本有很大差別,目前只支持兩個參數且有返回值。

²      unsigned long flags

一個與中斷管理相關的選項的位掩碼,共享、屏蔽之類的

²      const char *dev_name

這個傳遞給 request_irq 的字串用在 /proc/interrupts 來顯示中斷的擁有者

²      void *dev_id

用作共享中斷線的指針. 它是一個獨特的標識, 用在當釋放中斷線時以及可能還被驅動用來指向它自己的私有數據區(來標識哪個設備在中斷)。如果中斷沒有被共享, dev_id 可以設置爲 NULL, 但是使用這個項指向設備結構不管如何是個好主意。

request_irq()成功執行會返回0。如果返回非0值,就表示有錯誤發生,在這種情況下,指定的中斷處理程序不會被註冊。最常見的錯誤是一EBUSY,它表示給定的中斷線已經在使用(或者當前用戶沒有指定SA_ SHIRQ)。

注意,request_ irq函數可能會睡眠,因此,不能在中斷上下文或其他不允許阻塞的代碼中調用該函數。爲什麼request_irq會引起睡眠—這確實讓人費解。在註冊的過程中,內核需要在/proc/irq文件創建一個與中斷對應的項。函數proc_mkdirQ就是用來創建這個新的procfs項的。proc_makedir()通過調用函數proc_create()對這個新的profs項進行設置,proc_create()會調用函數kmalloc()來請求分配內存。

有一點很重要,初始化硬件和註冊中斷處理程序的順序必須正確,以防止中斷處理程序在設備初始化完成之前就開始執行。

在驅動程序初始化或者在設備第一次打開時,首先要調用該函數,以申請使用該irq。其中參數handler指的是要掛入到中斷請求隊列中的中斷服務例程。假定一個程序要對/dev/fd0/設備進行訪問,通常將IRQ6分配給軟盤控制器,給定這個中斷號6,軟盤驅動程序就可以發出下列請求,以將其中斷服務例程掛入中斷請求隊列:

request_irq(6, floppy_interrupt, SA_INTERRUPT|SA_SAMPLE_RANDOM, "floppy", NULL);

我們可以看到,floppy_interrupt()中斷服務例程運行時必須禁用中斷(設置了SA_INTERRUPT標誌),並且不允許共享這個IRQ(清SA_SHIRQ標誌)。

request_irq()函數的代碼在linux+v2.6.19/kernel/irq/manage.c中:

435int request_irq(unsigned int irq, irq_handler_thandler,

436                unsigned long irqflags, const char *devname, void *dev_id)

437{

438        struct irqaction *action;

439        int retval;

440

453if ((irqflags & IRQF_SHARED) && !dev_id) //共享中斷id不能爲NULL

454                return -EINVAL;

455        if (irq >= NR_IRQS)

456                return -EINVAL;

457        if (irq_desc[irq].status & IRQ_NOREQUEST)

458                return -EINVAL;

459        if (!handler)

460                return -EINVAL;

461

462action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC); //可能休眠

463        if (!action)

464                return -ENOMEM;

465//對action進行初始化

466action->handler = handler;

467action->flags = irqflags;

468cpus_clear(action->mask);

469action->name = devname;

470action->next = NULL;

471action->dev_id = dev_id;

472

473select_smp_affinity(irq); // SMP機器可設置中斷的親和力

474

475retval = setup_irq(irq, action); //掛接到全局的中斷irq_action

476        if (retval)

477kfree(action);

478

479        return retval;

480}

481EXPORT_SYMBOL(request_irq);

setup_irq

該函數爲內部函數,用戶驅動程序中一般不用,其纔是真正對中斷請求隊列進行初始化的函數

在系統初始化階段,內核爲了初始化時鐘中斷設備irq0描述符,在time_init( )函數中使用了下面的語句:

struct irqaction irq0  = {timer_interrupt, SA_INTERRUPT, 0, "timer", NULL,};

setup_irq(0, &irq0);

首先,初始化類型爲irqactionirq0變量,把handler域設置成timer_interrupt( )函數的地址,flags域設置成SA_INTERRUPTname域設置成"timer",最後一個域設置成NULL以表示沒有用dev_id值。接下來,內核調用setup_irq( ),把irq0插入到IRQ0的中斷請求隊列

215int setup_irq(unsigned int irq, struct irqaction *new)

216{

217        struct irq_desc *desc = irq_desc + irq;

218        struct irqaction *old, **p;

219        const char *old_name = NULL;

220        unsigned long flags;

221        int shared = 0;

222

223        if (irq >= NR_IRQS)

224                return -EINVAL;

225

226        if (desc->chip == &no_irq_chip)

227                return -ENOSYS;

244

248spin_lock_irqsave(&desc->lock, flags);

249p = &desc->action;

250old = *p;

251        if (old) {

252      /*雙方聲明爲共享中斷且觸發類型一致*/

258                if (!((old->flags & new->flags) & IRQF_SHARED) ||

259                    ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK)) {

260old_name = old->name;

261                        goto mismatch;

262                }

263

271                /* 找到IRQ隊列的尾部*/

272                do {

273p = &old->next;

274old = *p;

275                } while (old);

276shared = 1;

277        }

278

279        *p = new;

284if (!shared) {// 第一次插入action

285irq_chip_set_defaults(desc->chip);

286

287                /* 若指定了觸發類型則進行配置*/

288                if (new->flags & IRQF_TRIGGER_MASK) {

289                        if (desc->chip && desc->chip->set_type)

290desc->chip->set_type(irq, new->flags & IRQF_TRIGGER_MASK);

292                        else

297printk(KERN_WARNING "No IRQF_TRIGGER set_type ");

301                } else

302compat_irq_chip_set_default_handler(desc);

303

304desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING |

305IRQ_INPROGRESS);

306

307                if (!(desc->status & IRQ_NOAUTOEN)) {

308desc->depth = 0;

309desc->status &= ~IRQ_DISABLED;

310                        if (desc->chip->startup)

311desc->chip->startup(irq);

312                        else

313desc->chip->enable(irq);

314                } else

315                        /* Undo nested disables: */

316desc->depth = 1;

317        }

318spin_unlock_irqrestore(&desc->lock, flags);

319

320new->irq = irq;

321register_irq_proc(irq);

322new->dir = NULL;

323register_handler_proc(irq, new); //更新該中斷的proc記錄

324

325        return 0;

326

327mismatch:

328        if (!(new->flags & IRQF_PROBE_SHARED)) {

329printk(KERN_ERR "IRQ handler type mismatch for IRQ %d/n", irq);

330                if (old_name)

331printk(KERN_ERR "current handler: %s/n", old_name);

332dump_stack();

333        }

334spin_unlock_irqrestore(&desc->lock, flags);

335   return -EBUSY;

336}

1.1.2      中斷卸載free_irq

卸載驅動程序或者關閉設備時,需要註銷相應的中斷處理程序,並釋放中斷線。可以調用void free_irq(unsigned int irq,void *dev_ id)來釋放中斷線。

如果指定的中斷線不是共享的,那麼,該函數刪除處理程序的同時將禁用這條中斷線。如果中斷線是共享的,則僅刪除dev_id所對應的處理程序,而這條中斷線本身只有在刪除了最後一個處理程序時纔會被禁用。由此可以看出爲什麼惟一的dev_ id如此重要。對於共享的中斷線,需要一個惟一的信息來區分其上面的多個處理程序,並讓free_irq()僅僅刪除指定的處理程序。如果dev_id非空,它都必須與需要刪除的處理程序相匹配。非共享中斷,該域可以爲空,但需要和註冊時使用的指針一致。

直到該中斷線的任何中斷函數執行完畢其才返回,不能在中斷上下文調用函數free_ irq。例如,當軟盤操作終止時(或者終止對/dev/fd0/的I/O操作,或者卸載這個文件系統),驅動程序就放棄這個中斷號:

free_irq(6, NULL);

/linux+v2.6.19/kernel/irq/manage.c

352void free_irq(unsigned int irq, void *dev_id)

353{

354        struct irq_desc *desc;

355struct irqaction **p;

356        unsigned long flags;

357

358WARN_ON(in_interrupt());

359        if (irq >= NR_IRQS)

360                return;

361

362desc = irq_desc + irq;

363spin_lock_irqsave(&desc->lock, flags); //若當前中斷正在執行時,將無法獲得鎖,會死鎖,故不能在中斷上下文釋放中斷

364p = &desc->action;

365        for (;;) {

366                struct irqaction *action = *p;

367

368                if (action) {

369                        struct irqaction **pp = p;

370

371p = &action->next;

372if (action->dev_id != dev_id)

373                                continue;

374

375                        /* Found it - now remove it from the list of entries */

376 *pp = action->next;

377

384                        if (!desc->action) {//無其他中斷使用該中斷線則禁止

385desc->status |= IRQ_DISABLED;

386                                if (desc->chip->shutdown)

387desc->chip->shutdown(irq);

388                                else

389desc->chip->disable(irq);

390                        }

391spin_unlock_irqrestore(&desc->lock, flags);

392unregister_handler_proc(irq, action);

393

394                        /* Make sure it's not being used on another CPU */

395synchronize_irq(irq);

396kfree(action);

397                        return;

398                }

399printk(KERN_ERR "Trying to free already-free IRQ %d/n", irq);

400spin_unlock_irqrestore(&desc->lock, flags);

401                return;

402        }

403}

404EXPORT_SYMBOL(free_irq);

1.1.3      中斷控制

Linux內核提供了一組接口用於操作機器上的中斷狀態。這些接口爲我們提供了能夠禁止當前處理器的中斷系統,或屏蔽掉整個機器的一條中斷線的能力,這些例程都是與體系結構相關的,可以在<asm/system.h>和<asm/irq.h>中找到。

一般來說,控制中斷系統的原因歸根結底是需要提供同步。通過禁止中斷,可以確保某個中斷處理程序不會搶佔當前的代碼。此外,禁止中斷還可以禁止內核搶佔。然而,不管是禁止中斷還是禁止內核搶佔,都沒有提供任何保護機制來防止來自其他處理器的併發訪問。Linux支持多處理器,因此,內核代碼一般都需要獲取某種鎖,防止來自其他處理器對共享數據的併發訪問。獲取這些鎖的同時也可以禁止本地中斷。這樣鎖提供保護機制,防止來自其他處理器的併發訪問,而禁止中斷提供保護機制,則是防止來自其他中斷處理程序的併發訪問

1.1.3.1         禁止和激活本地全局中斷

用於禁止當前處理器(僅僅是當前處理器)上的本地中斷,隨後又激活它們的語句爲:

void local_irq_disable(void);

void local_irq_enable(void);

這兩個函數通常以單個彙編指令來實現(當然,這取決於體系結構)。在發出中斷的處理器上,它們將禁止和激活中斷的傳遞。

如果在調用local_irq_disable()例程之前已經禁止了中斷,那麼該例程往往會帶來潛在的危險;同樣相應的local_irq_enable()例程也存在潛在危險,因爲它將無條件地激活中斷,儘管這些中斷可能在開始時就是關閉的。所以我們需要一種機制把中斷恢復到以前的狀態而不是簡單地禁止或激活。內核普遍關心這點,是因爲內核中一個給定的代碼路徑既可以在中斷激活的情況下達到,也可以在中斷禁止的情況下達到,這取決於具體的調用鏈。因爲隨着內核的不斷增長,要想知道到達這個函數的所有代碼路徑將變得越來越困難,因此,在禁止中斷之前保存中斷系統的狀態會更加安全一些。相反,在準備激活中斷時,只需把中斷恢復到它們原來的狀態。

unsigned long flags;

void local_irq_save(unsigned long flags);

void local_irq_restore(unsigned long flags);

一個對 local_irq_save 的調用在當前處理器上禁止中斷遞交, 在保存當前中斷狀態到 flags 之後. 注意, flags 是直接傳遞, 不是通過指針。local_irq_restore恢復由 local_irq_save 存儲於 flags 的狀態, 而 local_irq_enable 無條件打開中斷。

這些方法至少部分要以的形式實現,因此表面上flags參數(這些參數必須定義爲unsigned long類型)是以值傳遞的。該參數包含具體體系結構的數據,也就是包含中斷系統的狀態。至少有一種體系結構把棧信息與值相結合(SPARC ),因此flags不能傳遞給另一個函數。基於這個原因,對local_irq_save()的調用和對local_irq_restore()的調用必須在同一個函數中進行。

前面的所有函數既可以在中斷中調用,也可以在進程上下文中調用。

不再使用全局的cli(),所有的中斷同步現在必須結合使用本地中斷控制和自旋鎖。這就意味着,爲了確保對共享數據的互斥訪問,以前代碼僅僅需要通過全局禁止中斷達到互斥,而現在則需要多做些工作了。

1.1.3.2         禁止指定中斷線

在某些情況下,只禁止整個系統中一條特定的中斷線就夠了。這就是所謂的屏蔽掉(masking out)一條中斷線。作爲例子,你可能想在對中斷的狀態操作之前禁止設備中斷的傳遞。爲此,Linux提供了四個接口:

/linux+v2.6.19/kernel/irq/manage

void disable_irq(unsigned int irq);

void enable_irq(unsigned int irq);

這兩個函數禁止中斷控制器上指定的中斷線,即禁止給定中斷向系統中所有處理器的傳遞。disable_irq函數只有在當前所有CPU上的該中斷的處理程序完成後,disable_irq才能返回。因此,調用者不僅要確保不在指定線上傳遞新的中斷,同時還要確保所有已經開始執行的處理程序已全部退出。

void disable_irq_nosync(unsigned int irq);

函數disable_irq_nosync()不會等待當前中斷處理程序執行完畢就禁止了該中斷,可用於中斷上下文

void synchronize_irq(unsigned int irq);

  17#ifdef CONFIG_SMP

  29void synchronize_irq(unsigned int irq)

  30{

  31        struct irq_desc *desc = irq_desc + irq;

  32

  33        if (irq >= NR_IRQS)

  34                return;

  35

  36        while (desc->status & IRQ_INPROGRESS)

  37cpu_relax();

  38}

  39EXPORT_SYMBOL(synchronize_irq);

  41#endif

synchronize_irq - SMP平臺有效,等到其他CPU上的中斷處理程序完畢

只有在所有的該中斷處理完畢後才返回,擁有該中斷所需要的資源時調用該函數可能導致死鎖,可在中斷上下文用

如果從中斷上下文中調用,就要特別小心!例如,當你正在處理一條中斷線時,並不想激活它。

禁止多箇中斷處理程序共享的中斷線是不合適的。禁止中斷線也就禁止了這條線上所有設備的中斷傳遞。因此,用於新設備的驅動程序應該傾向於不使用這些接口。

1.1.4      編寫中斷處理程序

每個中斷服務例程都有相同的原型,它的類型與request_irq參數中handler所要求的參數類型相匹配:

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

irqreturn_t irq_handler(int irq, void * dev_id)

參數:

²      IRQ:是這個處理程序要響應的中斷的中斷線號。如今,這個參數已經沒有太大用處了,可能只是在打印日誌信息時會用到。

²      dev_id: 設備標識符,其類型爲void*;dev_id是一個通用指針,它與在中斷處理程序註冊時傳遞給request_irq()的參數dev_ id必須一致。如果該值有惟一確定性(建議採用這樣的值,以便支持共享),那麼它就相當於一個cookie,可以用來區分共享同一中斷處理程序的多個設備。另外dev_ id也可能指向中斷處理程序使用的一個數據結構。因爲對每個設備而言,設備結構都是惟一的,而且可能在中斷處理程序中也用得到,因此,也通常會把設備結構傳遞給dev_id。

2.4內核中還有一個regs參數

static irqreturn_t intr_handler(int irq, void *dev-id,struct pt_regs *regs)

regs: 指向內核堆棧區的指針,該結構包含處理中斷之前處理器的寄存器和狀態。除了調試的時候,它們很少使用到。

中斷處理程序的返回值是一個特殊類型: irqreturn_t。中斷處理程序可能返回兩個特殊的值:

IRQ_ NONE和IRQ_ HANDLED。當中斷處理程序檢測到一箇中斷,但該中斷對應的設備並不是在註冊處理函數期間指定的產生源時,返回IRQ_ NONE;當中斷處理程序被正確調用,且確實是它所對應的設備產生了中斷時,返回IRQ_ HANDLED。另外,也可以使用宏IRQ_ RETVAL(x)。如果x爲非0值,那麼該宏返回IRQ_ HANDLED;否則,返回IRQ NONE。注意,irqreturn一這個返回類型實際上就是一個int型。之所以使用這些特殊值是爲了與早期的內核保持兼容—2.6版之前的內核並不支持這種特性,中斷處理程序只需返回void就行了。

中斷處理程序通常會標記爲static,因爲它從來不會被別的文件中的代碼直接調用。

1.1.5      重入和中斷處理程序

Linux中的中斷處理程序是無需重人的。當一個給定的中斷處理程序正在執行時,相應的中斷線在所有處理器上都會被屏蔽掉,以防止在同一中斷線上接收另一個新的中斷。通常情況下,所有其他的中斷都是打開的,所以這些不同中斷線上的其他中斷都能被處理,但當前中斷線總是被禁止的。由此可以看出,同一個中斷處理程序絕對不會被同時調用以處理嵌套的中斷。這極大地簡化了中斷處理程序的編寫。

1.1.6      共享的中斷處理程序

共享的處理程序與非共享的處理程序在註冊和運行方式上比較相似,但差異主要有以下三處:

²      request_ irq()的參數flags必須設置SA_ SHIRQ標誌。

²      對每個註冊的中斷處理程序來說,dev_ id參數必須惟一。指向任一設備結構的指針就可以滿足這一要求;通常會選擇設備結構,因爲它是惟一的,而且中斷處理程序可能會用到它。不能給共享的處理程序傳遞NULL值。

²      中斷處理程序必須能夠區分它的設備是否真的產生了中斷。這既需要硬件的支持,也需要處理程序中有相關的處理邏輯。如果硬件不支持這一功能,那中斷處理程序肯定會束手無策,它根本沒法知道到底是與它對應的設備發出了這個中斷,還是共享這條中斷線的其他設備發出了這個中斷。

所有共享中斷線的驅動程序都必須滿足以上要求。只要有任何一個設備沒有按規則進行共享,那麼中斷線就無法共享了。指定SA_SHIRQ標誌以調用request_irq()時,只有在以下兩種情況下才可能成功:中斷線當前未被註冊,或者在該線上的所有已註冊處理程序都指定了SA_SHIRQ。注意,在這一點上2.6與以前的內核是不同的,共享的處理程序可以混用SA INTERRUPT

內核接收一箇中斷後,它將依次調用在該中斷線上註冊的每一個處理程序。程序必須知道它是否應該爲這個中斷負責。因此,一個處理如果與它相關的設備並沒有產生中斷,那麼處理程序應該立即退出。這需要硬件設備提供狀態寄存器(或類似機制),以便中斷處理程序進行檢查。

1.1.7      中斷上下文

當執行一箇中斷處理程序或下半部時,內核處於中斷上下文(interrupt context)中。讓我們先回憶一下進程上下文。進程上下文是一種內核所處的操作模式,此時內核代表進程執行----例如,執行系統調用或運行內核線程。在進程上下文中,進程是以進程上下文的形式連接到內核中的,因此,可以通過current宏關聯當前進程。此外在進程上下文可以睡眠,也可以調用調度程序。

與之相反,中斷上下文和進程並沒有什麼瓜葛。current宏也是不相干的(儘管它會指向被中斷的進程)。因爲沒有進程的背景,所以中斷上下文不可以睡眠—否則又怎能再對它重新調度呢?因此,不能從中斷上下文中調用某些函數。如果一個函數睡眠,就不能在你的中斷處理程序中使用它。這是對什麼樣的函數可以在中斷處理程序中使用的限制。

中斷上下文具有較爲嚴格的時間限制,因爲它打斷了其他代碼。中斷上下文中的代碼應當迅速簡潔,儘量不要使用循環去處理繁重的工作。有一點非常重要,請永遠牢記;中斷處理程序打斷了其他的代碼(甚至可能是打斷了在其他中斷線上的另一中斷處理程序)。正是因爲這種異步執行的特性,所以所有的中斷處理程序必須儘可能的迅速、簡潔。儘量把工作從中斷處理程序中分離出來,放在下半部來執行,因爲下半部可以在更合適的時間運行。

中斷處理程序棧的設置是一個配置選項。曾經,中斷處理程序並不具有自己的棧。相反,它們共享所中斷進程的內核棧。內核棧的大小是兩頁,具體地說,在32位體系結構上是8KB,在64位體系結構上是16KB。因爲在這種設置中,中斷處理程序共享別人的堆棧,所以它們在棧中獲取空間時必須非常節省。

2.6早期的內核中,增加了一個選項,把棧的大小從兩頁減到一頁,也就是在32位的系統上只提供4KB的棧。這就減輕了內存的壓力,因爲系統中每個進程原先都需要兩頁不可換出的內核內存。爲了應對棧大小的減少,中斷處理程序擁有了自己的棧,每個處理器一個,大小爲一頁,這個棧就稱爲中斷棧

1.1.8      中斷系統的狀態

通常有必要了解中斷系統的狀態(例如中斷是禁止的還是激活的),或者你當前是否正處於中斷上下文的執行狀態中。

</linux/hardirq.h >中定義的兩個宏提供一個用來檢查內核的當前上下文的接口,它們是:

in_interrupt():是否處於硬件中斷或軟中斷上下文。如果內核處於中斷上下文中,它返回非0。說明內核此刻正在執行中斷處理程序,或者正在執行下半部處理程序。

in_irq():是否處於硬件中斷上下文。內核確實正在執行中斷處理程序時才返回非0

通常情況下,你要檢查自己是否處於進程上下文中。這種情況很常見,因爲代碼要做一些像睡眠這樣只能從進程上下文中做的事。此時應確保自己不在中斷上下文中。

linux+v2.6.19/include/linux/hardirq.h

  10/*

  11 * We put the hardirq and softirq counter into the preemption

  12 * counter. The bitmask has the following meaning:

  14 * - bits 0-7 are the preemption count (max preemption depth: 256)

  15 * - bits 8-15 are the softirq count (max # of softirqs: 256)

  19 * - bits 16-27 are the hardirq count (max # of hardirqs: 4096)

  20 * - ( bit 28 is the PREEMPT_ACTIVE flag. )

  21 *

  22 * PREEMPT_MASK: 0x000000ff

  23 * SOFTIRQ_MASK: 0x0000ff00

  24 * HARDIRQ_MASK: 0x0fff0000

  25 */

  63#define hardirq_count() (preempt_count() & HARDIRQ_MASK)

  64#define softirq_count() (preempt_count() & SOFTIRQ_MASK)

  65#define irq_count()     (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK))

////////////////////////////////

linux+v2.6.19/include/linux/preempt.h

  16# defineadd_preempt_count(val) do { preempt_count() += (val); } while (0)

  17# definesub_preempt_count(val) do { preempt_count() -= (val); } while (0)

  18#endif

  19

  20#define inc_preempt_count() add_preempt_count(1)

  21#define dec_preempt_count() sub_preempt_count(1)

  22

  23#define preempt_count() (current_thread_info()->preempt_count)

//////////////////////////////////

  67/*

  68 * Are we doing bottom half or hardware interrupt processing?

  69 * Are we in a softirq context? Interrupt context?

  70 */

  71#define in_irq()                (hardirq_count())

  72#define in_softirq()            (softirq_count())

  73#define in_interrupt()          (irq_count())

跟蹤中斷進入退出狀態,便於確定是否是中斷上下文

109#define irq_enter()                                     /

110        do {                                            /

111account_system_vtime(current);          /

112add_preempt_count(HARDIRQ_OFFSET);      /

113trace_hardirq_enter();                  /

114        } while (0)

115

116/*

117 * Exit irq context without processing softirqs:

118 */

119#define __irq_exit()                                    /

120        do {                                            /

121trace_hardirq_exit();                   /

122account_system_vtime(current);          /

123sub_preempt_count(HARDIRQ_OFFSET);      /

124        } while (0)

125

126/*

127 * Exit irq context and process softirqs if needed:

128 */

129extern void irq_exit(void);

1.1.9      中斷狀態/proc/interrupts

procfs是一個虛擬文件系統,它只存在於內核內存,一般安裝於/proc目錄下。在procfs中讀寫文件都要調用內核函數,這些函數模擬從真實文件中讀或寫。與此相關的例子是/proc/interrupts文件,該文件存放的是系統中與中斷相關的統計信息。下面是從單處理器PC上輸出的信息:

1列是中斷線。在這個系統中,現有的中斷號爲0-2, 4, 5, 12及15這裏沒有顯示沒有安裝處理程序的中斷線。

2列是一個接收中斷數目的計數器。事實上,系統中的每個處理器都存在這樣的列,但是,這個機器只有一個處理器。我們看到,時鐘中斷已接收3,602,371次中斷,這裏,聲卡(EMU 10K1)沒有接收一次中斷(這表示機器啓動以來還沒有使用它)。

3列是處理這個中斷的中斷控制器。

最後一列是與這個中斷相關的設備名字。這個名字是通過參數devname提供給函數request irq()的。如果中斷是共享的(例子中的4號中斷就是這種情況),則這條中斷線上註冊的所有設備都會列出來。

對於想深人探究procfs內部的人來說,procfs代碼位於fs/proc中。不必驚訝,提供/proc/interrupts的函數是與體系結構相關的,叫做show_interrupts()

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章