Linux核心模組程式設計--中斷處理程式(轉)

worldblog發表於2007-08-10
Linux核心模組程式設計--中斷處理程式(轉)[@more@]

  中斷處理程式

  除了上一章外,迄今為止,我們在核心中所做的每件事都是作為對一個程式請求的回應,要麼透過處理特殊的檔案,傳送 ioctl,要麼發出系統呼叫。但是核心的工作並不僅僅是回應程式請求。另一個每個位元組都很重要的工作是和連線到機器的硬體對話。

  在CPU和計算機的其他裝置之間有兩種互動作用。第一種是當CPU對硬體釋出命令時,另一種是當硬體要告訴CPU什麼事情時。第二種,被稱為中斷,實現起來是很困難的,因為它不得不處理什麼時間硬體是方便的而不是CPU。典型的硬體裝置只有很少的記憶體,如果當資訊可見的時候你不讀取的話它就會消失。

  在 Linux 下,硬體中斷被稱為 IRQs [Interrupt Requests (這是Linux起源的Intel 架構上的標準術語。 )的縮寫]。有兩種 IRQs,短的和長的。一個短的 IRQ 預期佔用 非常短的一段時間,在那期間,機器的剩餘部分被阻塞,沒有其他的中斷將被處理。長的 IRQ 佔用的時間長些,在那期間其他中斷有可能發生(但不能是來自同一裝置)。只要是可能的,宣告一個長中斷是較好的。

  當 CPU 接收到一箇中斷,它停止它正在做的任何事情(除非它正在處理一個更重要的中斷,在那種情況下,它將處理完那個中斷後才來處理現在的這個),在堆疊中儲存某些引數並呼叫中斷處理程式。這意味著在中斷處理程式自身中有些東西是不能允許的,因為系統處於一種未知的狀態。解決的辦法是中斷處理程式馬上做完需要做的,通常是從硬體裡面讀什麼或向硬體傳送什麼然後安排處理稍後的新資訊(這被稱為‘bottom half’)並返回。然後核心保證只要可能就呼叫bottom half --當這在執行,核心模組中允許做的所有事情將被允許。

  實現這個辦法是當接收到相關的IRQ(在 Intel 平臺下有16個)時去呼叫 request_irq 以使中斷處理程式被呼叫。 這個函式接收IRQ 號,函式名,標誌, /proc/interrupts 中的名字及一個傳送給中斷處理程式的引數作為其引數。標誌可以包括 SA_SHIRQ 以指明你願意和其他的中斷處理程式分享那個IRQ(通常因為幾個硬體裝置在同一IRQ)以及 SA_INTERRUPT 以指明這是一個快速中斷。這個函式只在那個IRQ上沒有處理程式的情況下成功,或者你願意兩者共享。

  然後從中斷處理程式中我們和硬體通訊,聯合tq_immediate使用 queue_task_irq 和 mark_bh(BH_IMMEDIATE) 排程 bottom half。我們在 2.0 版中不使用標準的queue_task 的原因是中斷有可能在其他人的 queue_task(queue_task_irq 從這被一個全域性鎖保護 -- 在 2.2 版中沒有queue_task_irq 而 queue_task 被一個鎖保護。 )中發生。我們需要 mark_bh 是因為Linux 的早期版本只能有32個 bottom half,而現在它們中的一個(BH_IMMEDIATE) 用於還沒有得到bottom half入口的驅動程式的bottom half連線表。

  Intel 架構鍵盤

  警告: 這章剩下的內容都特別指定為完全的 基於Intel 架構。如果你不是在這個平臺下執行,它沒有用。甚至不要試圖去編譯這裡的程式碼。

  在為這章寫範例程式碼的時候我有一個問題。一方面,對於一個有用的範例,它應該執行於每個人的計算機上且有意味深長的結果。另一方面,核心已經包含了所有的通用裝置的驅動程式,並且那些裝置驅動程式不能和我將要寫的共存。我發現的結果是寫一些鍵盤中斷的東西並且先關閉通常的鍵盤的中斷控制程式碼。因為在核心原始檔(明確的, drivers/char/keyboard.c)中它被定義為靜態符號,所以沒有辦法恢復它。如果你重視你的檔案系統,在 insmod 這些程式碼前,在另一個終端上sleep 120 ; reboot 。

  這個程式碼將自己繫結為 IRQ 1,這是Intel 架構下的鍵盤控制器的IRQ(中斷請求)。然後當它收到鍵盤中斷時它就讀鍵盤的狀態( 那就是inb(0x64)的目的)和掃描程式碼,該程式碼即是鍵盤的返回值。然後,核心一認為它是可行的它就執行給出鍵所使用的程式碼(掃描程式碼的前7位)和它是被按下(第8位為0)還是被釋放(第8位為1)的got_char函式。

  範例 intrpt.c

  /* intrpt.c - 中斷控制程式碼 */

/* Copyright (C) 1998 by Ori Pomerantz */

/* 必要標頭檔案 */

/* 標準標頭檔案 */

#include /* 核心工作 */

#include /* 明確指定是模組 */

/* 處理 CONFIG_MODVERSIONS */

#if CONFIG_MODVERSIONS==1

#define MODVERSIONS

#include

#endif

#include

#include

/* 我們想中斷 */

#include

#include

/* 在 2.2.3 版/usr/include/linux/version.h 中包含這個宏,

* 但 2.0.35 版不包含-因此在這加入以被需要 */

#ifndef KERNEL_VERSION

#define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c))

#endif

/* Bottom Half - 一旦核心模組認為它做任何事都是安全的時候這將被核心呼叫。 */

static void got_char(void *scancode)

{

printk("Scan Code %x %s. ",

(int) *((char *) scancode) & 0x7F,

*((char *) scancode) & 0x80 ? "Released" : "Pressed");

}

/* 這個函式為鍵盤中斷服務。它讀取來自鍵盤的相關資訊然後安排當核心認為bottom half安全的時候讓它執行 */

void irq_handler(int irq,

void *dev_id,

struct pt_regs *regs)

{

/* 這些變數是靜態的,因為它們需要對 bottom half 可見(透過指標)。 */

static unsigned char scancode;

static struct tq_struct task =

{NULL, 0, got_char, &scancode};

unsigned char status;

/* Read keyboard status */

status = inb(0x64);

scancode = inb(0x60);

/* 安排 bottom half 執行 */

#if LINUX_VERSION_CODE > KERNEL_VERSION(2,2,0)

queue_task(&task, &tq_immediate);

#else

queue_task_irq(&task, &tq_immediate);

#endif

mark_bh(IMMEDIATE_BH);

}

/* 初始化模組--登記 IRQ 控制程式碼 */

int init_module()

{

/* 既然鍵盤的控制程式碼不能和我們的共存,在我們做事情前我們不得不關閉它(釋放它的 IRQ)。

* 因為我們不知道它在哪兒,所以以後沒有辦法恢復它--因此當我們做完時計算機將被重新啟動。

*/

free_irq(1, NULL);

/* 請求 IRQ 1,鍵盤的 IRQ,指向我們的 irq_handler。 */

return request_irq(

1, /* PC上的鍵盤的 IRQ 號 */

irq_handler, /* 我們的控制程式碼 */

SA_SHIRQ,

/* SA_SHIRQ 意味著我們將另一個控制程式碼用於這個 IRQ。

*

* SA_INTERRUPT 能使控制程式碼為一個快速中斷。

*/

"test_keyboard_irq_handler", NULL);

}

/* 清除 */

void cleanup_module()

{

/* 它在這兒只是為了完全。它是完全不相關的,因為我們沒有辦法恢復通常的鍵盤中斷因此計算機完全沒用 * 了,需要被重新啟動。 */

free_irq(1, NULL);

}

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-940219/,如需轉載,請註明出處,否則將追究法律責任。

相關文章