在一個系統中,中斷時常發生,而且線程調度也是由一個硬件定時器時時刻刻發出中斷來支撐的??梢哉f中斷就是linux系統的靈魂。
由于中斷來源很多,加上一個硬件系統中會存在許多中斷控制器級聯,因此會產生中斷管理上的麻煩,Linux管理中斷也是比較復雜,用到了很多數據結構,如果一開始就深究這些數據結構實例如何組織,只能是事倍功半,因此先進行情景分析,看看當一個中斷在設備產生后,會發生什么事情。
一、中斷控制器的級聯情況
下面一張圖可以涵蓋兩種級聯情況
圖中的GIC、GPC、GPIO控制器都能作為中斷控制器,其中我認為GIC才是硬件上真正意義的中斷控制器,而其他兩個之所以能被稱為中斷控制器,是因為他們也需要分辨下一級的中斷源。
還可以看到這里基本忽略了SGI和PPI中斷,因為這些中斷很可能不來自于設備。
一對一的級聯
從GPC到GIC的中斷,是一對一的。這種有個特點就是GIC發生了中斷后,無需判斷是下一級哪個GPC的哪個中斷發生了。舉個例子,如果是GIC 的128號中斷發生了,無需再判斷是GPC哪個中斷傳遞給了GIC的128號中斷,直接就能斷定是GPC對應的128號中斷產生了。
多對一的級聯
從GPIO到GPC的5號中斷,是多對一的關系。這個源自于GPIO的一個特點,就是有可能多個GPIO信號只能聯合產生一個中斷給到上一級中斷控制器,這里上一級的中斷控制器就是GPC。舉個例子,假如GIC發生了中斷,讀取GIC寄存器知道發生了GIC的122號中斷,那么也就知道了是GPC的5號中斷產生了,已知GPIO會導致GPC5號中斷產生,但是是哪個GPIO的產生呢?那么就需要在GPIO控制器的驅動程序中分辨一下。
以上兩種級聯的情況,決定了當一個中斷產生后,途徑的中斷控制器的中斷執行過程。
二、linux對中斷號的管理
通常如果一個設備要作為一個中斷源,會在設備樹指定使用到哪個中斷控制器,使用到這個中斷控制器里面的第x個中斷。但是這個數字并不能作為唯一標識這個中斷源的號碼,原因在于同一個中斷源,在gpio上會表示x號中斷,而在gpc上面,則可能是y號中斷。
舉個例子,比如有個按鍵key使用到了gpio中斷號為4的,gpio控制器會向上gpc產生一個的中斷號為5,同一個中斷,一下子從4變成5,那么我們指定的4 和5都具有很強的硬件關系,并不能給到linux去標識唯一的中斷。
因此,linux中的中斷號稱為linux irq,是一個系統軟件上的索引。在編寫設備驅動的時候,都會根據這個irq去申請中斷并且注冊中斷服務函數。
linux irq如何轉換?
linux irq實際上是在系統初始化,在解析設備樹的時候根據在設備樹定義的中斷信息將和硬件相關的中斷號轉化成linux irq。
以上是對于按鍵編寫的設備樹節點,使用到GPIO1中斷控制器,并且用到了GPIO的18號中斷,觸發中斷類型是雙邊沿。那么在解析設備樹的時候,18的硬件中斷號就會被轉換成唯一標識這個中斷的linux irq。至于解析過程涉及代碼比較多,這里只是情景分析,就不詳細追代碼了。
三、中斷描述符
這里還要介紹一下中斷描述符這個數據結構。一個linux irq對應一個中斷描述符irq_desc,通過linux irq就能找到對應的中斷描述符。看一下這個數據結構可以大概知道中斷描述符有什么用
irq_desc
struct irq_desc {
struct irq_data irq_data;
unsigned int __percpu *kstat_irqs;
irq_flow_handler_t handle_irq;
#ifdef CONFIG_IRQ_PREFLOW_FASTEOI
irq_preflow_handler_t preflow_handler;
#endif
struct irqaction *action; /* IRQ action list */
unsigned int status_use_accessors;
unsigned int core_internal_state__do_not_mess_with_it;
unsigned int depth; /* nested irq disables */
unsigned int wake_depth; /* nested wake enables */
unsigned int irq_count; /* For detecting broken IRQs */
unsigned long last_unhandled; /* Aging timer for unhandled count */
unsigned int irqs_unhandled;
atomic_t threads_handled;
int threads_handled_last;
raw_spinlock_t lock;
struct cpumask *percpu_enabled;
#ifdef CONFIG_SMP
const struct cpumask *affinity_hint;
struct irq_affinity_notify *affinity_notify;
#ifdef CONFIG_GENERIC_PENDING_IRQ
cpumask_var_t pending_mask;
#endif
#endif
unsigned long threads_oneshot;
atomic_t threads_active;
wait_queue_head_t wait_for_threads;
#ifdef CONFIG_PM_SLEEP
unsigned int nr_actions;
unsigned int no_suspend_depth;
unsigned int cond_suspend_depth;
unsigned int force_resume_depth;
#endif
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;
#endif
int parent_irq;
struct module *owner;
const char *name;
} ____cacheline_internodealigned_in_smp;