日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

Linux 中断之中断处理浅析

發(fā)布時(shí)間:2025/4/5 linux 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux 中断之中断处理浅析 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1. 中斷的概念

中斷是指在CPU正常運(yùn)行期間,由于內(nèi)外部事件或由程序預(yù)先安排的事件引起的 CPU 暫時(shí)停止正在運(yùn)行的程序,轉(zhuǎn)而為該內(nèi)部或外部事件或預(yù)先安排的事件服務(wù)的程序中去,服務(wù)完畢后再返回去繼續(xù)運(yùn)行被暫時(shí)中斷的程序。Linux中通常分為外部中斷(又叫硬件中斷)和內(nèi)部中斷(又叫異常)。

軟件對(duì)硬件進(jìn)行配置后,軟件期望等待硬件的某種狀態(tài)(比如,收到了數(shù)據(jù)),這里有兩種方式,一種是輪詢(polling): CPU 不斷的去讀硬件狀態(tài)。另一種是當(dāng)硬件完成某種事件后,給 CPU 一個(gè)中斷,讓 CPU 停下手上的事情,去處理這個(gè)中斷。很顯然,中斷的交互方式提高了系統(tǒng)的吞吐。

當(dāng) CPU 收到一個(gè)中斷 (IRQ)的時(shí)候,會(huì)去執(zhí)行該中斷對(duì)應(yīng)的處理函數(shù)(ISR)。普通情況下,會(huì)有一個(gè)中斷向量表,向量表中定義了 CPU 對(duì)應(yīng)的每一個(gè)外設(shè)資源的中斷處理程序的入口,當(dāng)發(fā)生對(duì)應(yīng)的中斷的時(shí)候, CPU 直接跳轉(zhuǎn)到這個(gè)入口執(zhí)行程序。也就是中斷上下文。(注意:中斷上下文中,不可阻塞睡眠)。

?

2. Linux 中斷 top/bottom

玩過 MCU 的人都知道,中斷服務(wù)程序的設(shè)計(jì)最好是快速完成任務(wù)并退出,因?yàn)榇丝滔到y(tǒng)處于被中斷中。但是在 ISR 中又有一些必須完成的事情,比如:清中斷標(biāo)志,讀/寫數(shù)據(jù),寄存器操作等。

在 Linux 中,同樣也是這個(gè)要求,希望盡快的完成 ISR。但事與愿違,有些 ISR 中任務(wù)繁重,會(huì)消耗很多時(shí)間,導(dǎo)致響應(yīng)速度變差。Linux 中針對(duì)這種情況,將中斷分為了兩部分:

1. 上半部(top half):收到一個(gè)中斷,立即執(zhí)行,有嚴(yán)格的時(shí)間限制,只做一些必要的工作,比如:應(yīng)答,復(fù)位等。這些工作都是在所有中斷被禁止的情況下完成的

2. 底半部(bottom half):能夠被推遲到后面完成的任務(wù)會(huì)在底半部進(jìn)行。在適合的時(shí)機(jī),下半部會(huì)被開中斷執(zhí)行。(具體的機(jī)制在下一篇文章分析)

?

3. 中斷處理程序

驅(qū)動(dòng)程序可以使用接口:

  • request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,

  • const char *name, void *dev)

  • 像系統(tǒng)申請(qǐng)注冊(cè)一個(gè)中斷處理程序。

    其中的參數(shù):

    參數(shù)含義
    irq表了該中斷的中斷號(hào),一般 CPU 的中斷號(hào)都會(huì)事先定義好。
    handler中斷發(fā)生后的 ISR
    flags中斷標(biāo)志( IRQF_DISABLED / IRQFSAMPLE_RANDOM / IRQF_TIMER / IRQF_SHARED)
    name?中斷相關(guān)的設(shè)備 ASCII 文本,例如 "keyboard",這些名字會(huì)在 /proc/irq 和 /proc/interrupts 文件使用
    dev用于共享中斷線,傳遞驅(qū)動(dòng)程序的設(shè)備結(jié)構(gòu)。非共享類型的中斷,直接設(shè)置成為 NULL

    ?

    ?

    ?

    ?

    ?

    ?

    ?

    中斷標(biāo)志 flag 的含義:

    標(biāo)志含義
    IRQF_DISABLED設(shè)置這個(gè)標(biāo)志的話,意味著內(nèi)核在處理這個(gè) ISR 期間,要禁止其他中斷(多數(shù)情況不使用這個(gè))
    IRQFSAMPLE_RANDOM表明這個(gè)設(shè)備產(chǎn)生的中斷對(duì)內(nèi)核熵池有貢獻(xiàn)
    IRQF_TIMER為系統(tǒng)定時(shí)器準(zhǔn)備的標(biāo)志
    IRQF_SHARED表明多個(gè)中斷處理程序之間共享中斷線。同一個(gè)給定的線上注冊(cè)每個(gè)處理程序,必須設(shè)置這個(gè)

    ?

    ?

    ?

    ?

    ?

    ?

    調(diào)用?request _irq?成功執(zhí)行返回 0。常見錯(cuò)誤是?-EBUSY,表示給定的中斷線已經(jīng)在使用(或者沒有指定 IRQF_SHARED)

    注意:request_irq?函數(shù)可能引起睡眠,所以不允許在中斷上下文或者不允許睡眠的代碼中調(diào)用

    ?

    釋放中斷:

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

    用于釋放中斷處理函數(shù)。

    注意:Linux 中的中斷處理程序是無須重入的。當(dāng)給定的中斷處理程序正在執(zhí)行的時(shí)候,其中斷線在所有的處理器上都會(huì)被屏蔽掉,以防在同一個(gè)中斷線上又接收到另一個(gè)新的中斷。通常情況下,除了該中斷的其他中斷都是打開的,也就是說其他的中斷線上的重點(diǎn)都能夠被處理,但是當(dāng)前的中斷線總是被禁止的,故,同一個(gè)中斷處理程序是絕對(duì)不會(huì)被自己嵌套的

    ?

    4. 中斷上下文

    與進(jìn)程上下文不一樣,內(nèi)核執(zhí)行中斷服務(wù)程序的時(shí)候,處于中斷上下文。中斷處理程序并沒有自己的獨(dú)立的棧,而是使用了內(nèi)核棧,其大小一般是有限制的(32bit 機(jī)器 8KB)。所以其必須短小精悍。同時(shí)中斷服務(wù)程序是打斷了正常的程序流程,這一點(diǎn)上也必須保證快速的執(zhí)行。同時(shí)中斷上下文中是不允許睡眠,阻塞的。

    中斷上下文不能睡眠的原因是

    1、 中斷處理的時(shí)候,不應(yīng)該發(fā)生進(jìn)程切換,因?yàn)樵谥袛郼ontext中,唯一能打斷當(dāng)前中斷handler的只有更高優(yōu)先級(jí)的中斷,它不會(huì)被進(jìn)程打斷,如果在 中斷context中休眠,則沒有辦法喚醒它,因?yàn)樗械膚ake_up_xxx都是針對(duì)某個(gè)進(jìn)程而言的,而在中斷context中,沒有進(jìn)程的概念,沒 有一個(gè)task_struct(這點(diǎn)對(duì)于softirq和tasklet一樣),因此真的休眠了,比如調(diào)用了會(huì)導(dǎo)致block的例程,內(nèi)核幾乎肯定會(huì)死。

    2、schedule()在切換進(jìn)程時(shí),保存當(dāng)前的進(jìn)程上下文(CPU寄存器的值、進(jìn)程的狀態(tài)以及堆棧中的內(nèi)容),以便以后恢復(fù)此進(jìn)程運(yùn)行。中斷發(fā)生后,內(nèi)核會(huì)先保存當(dāng)前被中斷的進(jìn)程上下文(在調(diào)用中斷處理程序后恢復(fù));

    但在中斷處理程序里,CPU寄存器的值肯定已經(jīng)變化了吧(最重要的程序計(jì)數(shù)器PC、堆棧SP等),如果此時(shí)因?yàn)樗呋蜃枞僮髡{(diào)用了schedule(),則保存的進(jìn)程上下文就不是當(dāng)前的進(jìn)程context了.所以不可以在中斷處理程序中調(diào)用schedule()。

    3、內(nèi)核中schedule()函數(shù)本身在進(jìn)來的時(shí)候判斷是否處于中斷上下文:

    if(unlikely(in_interrupt()))

    BUG();

    因此,強(qiáng)行調(diào)用schedule()的結(jié)果就是內(nèi)核BUG。

    4、中斷handler會(huì)使用被中斷的進(jìn)程內(nèi)核堆棧,但不會(huì)對(duì)它有任何影響,因?yàn)閔andler使用完后會(huì)完全清除它使用的那部分堆棧,恢復(fù)被中斷前的原貌。

    5、處于中斷context時(shí)候,內(nèi)核是不可搶占的。因此,如果休眠,則內(nèi)核一定掛起。

    ?

    5. 舉例

    比如 RTC 驅(qū)動(dòng)程序 (drivers/char/rtc.c)。在 RTC 驅(qū)動(dòng)的初始化階段,會(huì)調(diào)用到 rtc_init 函數(shù):

    module_init(rtc_init);

    在這個(gè)初始化函數(shù)中調(diào)用到了 request_irq 用于申請(qǐng)中斷資源,并注冊(cè)服務(wù)程序:

  • static int __init rtc_init(void)

  • {

  • ...

  • rtc_int_handler_ptr = rtc_interrupt;

  • ...

  • request_irq(RTC_IRQ, rtc_int_handler_ptr, 0, "rtc", NULL)

  • ...

  • }

  • RTC_IRQ 是中斷號(hào),和處理器綁定。

    rtc_interrupt 是中斷處理程序:

  • static irqreturn_t rtc_interrupt(int irq, void *dev_id)

  • {

  • /*

  • * Can be an alarm interrupt, update complete interrupt,

  • * or a periodic interrupt. We store the status in the

  • * low byte and the number of interrupts received since

  • * the last read in the remainder of rtc_irq_data.

  • */

  • ?
  • spin_lock(&rtc_lock);

  • rtc_irq_data += 0x100;

  • rtc_irq_data &= ~0xff;

  • if (is_hpet_enabled()) {

  • /*

  • * In this case it is HPET RTC interrupt handler

  • * calling us, with the interrupt information

  • * passed as arg1, instead of irq.

  • */

  • rtc_irq_data |= (unsigned long)irq & 0xF0;

  • } else {

  • rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0);

  • }

  • ?
  • if (rtc_status & RTC_TIMER_ON)

  • mod_timer(&rtc_irq_timer, jiffies + HZ/rtc_freq + 2*HZ/100);

  • ?
  • spin_unlock(&rtc_lock);

  • ?
  • wake_up_interruptible(&rtc_wait);

  • ?
  • kill_fasync(&rtc_async_queue, SIGIO, POLL_IN);

  • ?
  • return IRQ_HANDLED;

  • }

  • 每次收到 RTC 中斷,就會(huì)調(diào)用進(jìn)這個(gè)函數(shù)。

    ?

    6. 中斷處理流程

    發(fā)生中斷時(shí),CPU執(zhí)行異常向量vector_irq的代碼, 即異常向量表中的中斷異常的代碼,它是一個(gè)跳轉(zhuǎn)指令,跳去執(zhí)行真正的中斷處理程序,在vector_irq里面,最終會(huì)調(diào)用中斷處理的總?cè)肟诤瘮?shù)。

    C 語(yǔ)言的入口為 :?asm_do_IRQ(unsigned int irq, struct pt_regs *regs)

  • asmlinkage void __exception_irq_entry

  • asm_do_IRQ(unsigned int irq, struct pt_regs *regs)

  • {

  • handle_IRQ(irq, regs);

  • }

  • 該函數(shù)的入?yún)?irq 為中斷號(hào)。

    asm_do_IRQ -> handle_IRQ

  • void handle_IRQ(unsigned int irq, struct pt_regs *regs)

  • {

  • __handle_domain_irq(NULL, irq, false, regs);

  • }

  • handle_IRQ ->?__handle_domain_irq

  • int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,

  • bool lookup, struct pt_regs *regs)

  • {

  • struct pt_regs *old_regs = set_irq_regs(regs);

  • unsigned int irq = hwirq;

  • int ret = 0;

  • ?
  • irq_enter();

  • ?
  • #ifdef CONFIG_IRQ_DOMAIN

  • if (lookup)

  • irq = irq_find_mapping(domain, hwirq);

  • #endif

  • ?
  • /*

  • * Some hardware gives randomly wrong interrupts. Rather

  • * than crashing, do something sensible.

  • */

  • if (unlikely(!irq || irq >= nr_irqs)) {

  • ack_bad_irq(irq);

  • ret = -EINVAL;

  • } else {

  • generic_handle_irq(irq);

  • }

  • ?
  • irq_exit();

  • set_irq_regs(old_regs);

  • return ret;

  • }

  • 這里請(qǐng)注意:

    先調(diào)用了 irq_enter 標(biāo)記進(jìn)入了硬件中斷:

    irq_enter是更新一些系統(tǒng)的統(tǒng)計(jì)信息,同時(shí)在__irq_enter宏中禁止了進(jìn)程的搶占。雖然在產(chǎn)生IRQ時(shí),ARM會(huì)自動(dòng)把CPSR中的I位置位,禁止新的IRQ請(qǐng)求,直到中斷控制轉(zhuǎn)到相應(yīng)的流控層后才通過local_irq_enable()打開。那為何還要禁止搶占?這是因?yàn)橐紤]中斷嵌套的問題,一旦流控層或驅(qū)動(dòng)程序主動(dòng)通過local_irq_enable打開了IRQ,而此時(shí)該中斷還沒處理完成,新的irq請(qǐng)求到達(dá),這時(shí)代碼會(huì)再次進(jìn)入irq_enter,在本次嵌套中斷返回時(shí),內(nèi)核不希望進(jìn)行搶占調(diào)度,而是要等到最外層的中斷處理完成后才做出調(diào)度動(dòng)作,所以才有了禁止搶占這一處理

    再調(diào)用?generic_handle_irq

    最后調(diào)用 irq_exit 刪除進(jìn)入硬件中斷的標(biāo)記

    __handle_domain_irq ->?generic_handle_irq

  • int generic_handle_irq(unsigned int irq)

  • {

  • struct irq_desc *desc = irq_to_desc(irq);

  • ?
  • if (!desc)

  • return -EINVAL;

  • generic_handle_irq_desc(desc);

  • return 0;

  • }

  • EXPORT_SYMBOL_GPL(generic_handle_irq);

  • 首先在函數(shù)?irq_to_desc?中根據(jù)發(fā)生中斷的中斷號(hào),去取出它的 irq_desc 中斷描述結(jié)構(gòu),然后調(diào)用?generic_handle_irq_desc

  • static inline void generic_handle_irq_desc(struct irq_desc *desc)

  • {

  • desc->handle_irq(desc);

  • }

  • 這里調(diào)用了 handle_irq 函數(shù)。

    所以,在上述流程中,還需要分析?irq_to_desc??流程:

  • struct irq_desc *irq_to_desc(unsigned int irq)

  • {

  • return (irq < NR_IRQS) ? irq_desc + irq : NULL;

  • }

  • EXPORT_SYMBOL(irq_to_desc);

  • NR_IRQS 是支持的總的中斷個(gè)數(shù),當(dāng)然,irq 不能夠大于這個(gè)數(shù)目。所以返回 irq_desc + irq。

    irq_desc 是一個(gè)全局的數(shù)組:

  • struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {

  • [0 ... NR_IRQS-1] = {

  • .handle_irq = handle_bad_irq,

  • .depth = 1,

  • .lock = __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock),

  • }

  • };

  • 這里是這個(gè)數(shù)組的初始化的地方。所有的?handle_irq?函數(shù)都被初始化成為了?handle_bad_irq。

    細(xì)心的觀眾可能發(fā)現(xiàn)了,調(diào)用這個(gè)?desc->handle_irq(desc)?函數(shù),并不是咱們注冊(cè)進(jìn)去的中斷處理函數(shù)啊,因?yàn)閮蓚€(gè)函數(shù)的原型定義都不一樣。這個(gè)?handle_irq?是?irq_flow_handler_t?類型,而我們注冊(cè)進(jìn)去的服務(wù)程序是?irq_handler_t,這兩個(gè)明顯不是同一個(gè)東西,所以這里我們還需要繼續(xù)分析。

    ?

    6.1 中斷相關(guān)的數(shù)據(jù)結(jié)構(gòu)

    Linux 中斷相關(guān)的數(shù)據(jù)結(jié)構(gòu)有 3 個(gè)

    結(jié)構(gòu)名稱作用
    irq_descIRQ 的軟件層面上的資源描述
    irqactionIRQ 的通用操作
    irq_chip對(duì)應(yīng)每個(gè)芯片的具體實(shí)現(xiàn)

    ?

    ?

    ?

    ?

    ?

    irq_desc 結(jié)構(gòu)如下

  • struct irq_desc {

  • struct irq_common_data irq_common_data;

  • 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;

  • const struct cpumask *percpu_affinity;

  • #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

  • #ifdef CONFIG_GENERIC_IRQ_DEBUGFS

  • struct dentry *debugfs_file;

  • const char *dev_name;

  • #endif

  • #ifdef CONFIG_SPARSE_IRQ

  • struct rcu_head rcu;

  • struct kobject kobj;

  • #endif

  • struct mutex request_mutex;

  • int parent_irq;

  • struct module *owner;

  • const char *name;

  • } ____cacheline_internodealigned_in_smp;

  • ?

    irqaction 結(jié)構(gòu)如下:

  • /**

  • * struct irqaction - per interrupt action descriptor

  • * @handler: interrupt handler function

  • * @name: name of the device

  • * @dev_id: cookie to identify the device

  • * @percpu_dev_id: cookie to identify the device

  • * @next: pointer to the next irqaction for shared interrupts

  • * @irq: interrupt number

  • * @flags: flags (see IRQF_* above)

  • * @thread_fn: interrupt handler function for threaded interrupts

  • * @thread: thread pointer for threaded interrupts

  • * @secondary: pointer to secondary irqaction (force threading)

  • * @thread_flags: flags related to @thread

  • * @thread_mask: bitmask for keeping track of @thread activity

  • * @dir: pointer to the proc/irq/NN/name entry

  • */

  • struct irqaction {

  • irq_handler_t handler;

  • void *dev_id;

  • void __percpu *percpu_dev_id;

  • struct irqaction *next;

  • irq_handler_t thread_fn;

  • struct task_struct *thread;

  • struct irqaction *secondary;

  • unsigned int irq;

  • unsigned int flags;

  • unsigned long thread_flags;

  • unsigned long thread_mask;

  • const char *name;

  • struct proc_dir_entry *dir;

  • } ____cacheline_internodealigned_in_smp;

  • ?

    irq_chip 描述如下:

  • /**

  • * struct irq_chip - hardware interrupt chip descriptor

  • *

  • * @parent_device: pointer to parent device for irqchip

  • * @name: name for /proc/interrupts

  • * @irq_startup: start up the interrupt (defaults to ->enable if NULL)

  • * @irq_shutdown: shut down the interrupt (defaults to ->disable if NULL)

  • * @irq_enable: enable the interrupt (defaults to chip->unmask if NULL)

  • * @irq_disable: disable the interrupt

  • * @irq_ack: start of a new interrupt

  • * @irq_mask: mask an interrupt source

  • * @irq_mask_ack: ack and mask an interrupt source

  • * @irq_unmask: unmask an interrupt source

  • * @irq_eoi: end of interrupt

  • * @irq_set_affinity: Set the CPU affinity on SMP machines. If the force

  • * argument is true, it tells the driver to

  • * unconditionally apply the affinity setting. Sanity

  • * checks against the supplied affinity mask are not

  • * required. This is used for CPU hotplug where the

  • * target CPU is not yet set in the cpu_online_mask.

  • * @irq_retrigger: resend an IRQ to the CPU

  • * @irq_set_type: set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ

  • * @irq_set_wake: enable/disable power-management wake-on of an IRQ

  • * @irq_bus_lock: function to lock access to slow bus (i2c) chips

  • * @irq_bus_sync_unlock:function to sync and unlock slow bus (i2c) chips

  • * @irq_cpu_online: configure an interrupt source for a secondary CPU

  • * @irq_cpu_offline: un-configure an interrupt source for a secondary CPU

  • * @irq_suspend: function called from core code on suspend once per

  • * chip, when one or more interrupts are installed

  • * @irq_resume: function called from core code on resume once per chip,

  • * when one ore more interrupts are installed

  • * @irq_pm_shutdown: function called from core code on shutdown once per chip

  • * @irq_calc_mask: Optional function to set irq_data.mask for special cases

  • * @irq_print_chip: optional to print special chip info in show_interrupts

  • * @irq_request_resources: optional to request resources before calling

  • * any other callback related to this irq

  • * @irq_release_resources: optional to release resources acquired with

  • * irq_request_resources

  • * @irq_compose_msi_msg: optional to compose message content for MSI

  • * @irq_write_msi_msg: optional to write message content for MSI

  • * @irq_get_irqchip_state: return the internal state of an interrupt

  • * @irq_set_irqchip_state: set the internal state of a interrupt

  • * @irq_set_vcpu_affinity: optional to target a vCPU in a virtual machine

  • * @ipi_send_single: send a single IPI to destination cpus

  • * @ipi_send_mask: send an IPI to destination cpus in cpumask

  • * @flags: chip specific flags

  • */

  • struct irq_chip {

  • struct device *parent_device;

  • const char *name;

  • unsigned int (*irq_startup)(struct irq_data *data);

  • void (*irq_shutdown)(struct irq_data *data);

  • void (*irq_enable)(struct irq_data *data);

  • void (*irq_disable)(struct irq_data *data);

  • ?
  • void (*irq_ack)(struct irq_data *data);

  • void (*irq_mask)(struct irq_data *data);

  • void (*irq_mask_ack)(struct irq_data *data);

  • void (*irq_unmask)(struct irq_data *data);

  • void (*irq_eoi)(struct irq_data *data);

  • ?
  • int (*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force);

  • int (*irq_retrigger)(struct irq_data *data);

  • int (*irq_set_type)(struct irq_data *data, unsigned int flow_type);

  • int (*irq_set_wake)(struct irq_data *data, unsigned int on);

  • ?
  • void (*irq_bus_lock)(struct irq_data *data);

  • void (*irq_bus_sync_unlock)(struct irq_data *data);

  • ?
  • void (*irq_cpu_online)(struct irq_data *data);

  • void (*irq_cpu_offline)(struct irq_data *data);

  • ?
  • void (*irq_suspend)(struct irq_data *data);

  • void (*irq_resume)(struct irq_data *data);

  • void (*irq_pm_shutdown)(struct irq_data *data);

  • ?
  • void (*irq_calc_mask)(struct irq_data *data);

  • ?
  • void (*irq_print_chip)(struct irq_data *data, struct seq_file *p);

  • int (*irq_request_resources)(struct irq_data *data);

  • void (*irq_release_resources)(struct irq_data *data);

  • ?
  • void (*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg);

  • void (*irq_write_msi_msg)(struct irq_data *data, struct msi_msg *msg);

  • ?
  • int (*irq_get_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool *state);

  • int (*irq_set_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool state);

  • ?
  • int (*irq_set_vcpu_affinity)(struct irq_data *data, void *vcpu_info);

  • ?
  • void (*ipi_send_single)(struct irq_data *data, unsigned int cpu);

  • void (*ipi_send_mask)(struct irq_data *data, const struct cpumask *dest);

  • ?
  • unsigned long flags;

  • };

  • irq_chip?是一串和芯片相關(guān)的函數(shù)指針,這里定義的非常的全面,基本上和 IRQ 相關(guān)的可能出現(xiàn)的操作都全部定義進(jìn)去了,具體根據(jù)不同的芯片,需要在不同的芯片的地方去初始化這個(gè)結(jié)構(gòu),然后這個(gè)結(jié)構(gòu)會(huì)嵌入到通用的 IRQ 處理軟件中去使用,使得軟件處理邏輯和芯片邏輯完全的分開。

    好,我們接下來繼續(xù)前進(jìn)。

    ?

    6.2 初始化 Chip 相關(guān)的 IRQ

    眾所周知,啟動(dòng)的時(shí)候,C 語(yǔ)言從?start_kernel?開始,在這里面,調(diào)用了和 machine 相關(guān)的 IRQ 的初始化?init_IRQ()

  • asmlinkage __visible void __init start_kernel(void)

  • {

  • char *command_line;

  • char *after_dashes;

  • ?
  • .....

  • ?
  • early_irq_init();

  • init_IRQ();

  • ?
  • .....

  • ?
  • }

  • 在 init_IRQ 中,調(diào)用了?machine_desc->init_irq()

  • void __init init_IRQ(void)

  • {

  • int ret;

  • ?
  • if (IS_ENABLED(CONFIG_OF) && !machine_desc->init_irq)

  • irqchip_init();

  • else

  • machine_desc->init_irq();

  • ?
  • if (IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_CACHE_L2X0) &&

  • (machine_desc->l2c_aux_mask || machine_desc->l2c_aux_val)) {

  • if (!outer_cache.write_sec)

  • outer_cache.write_sec = machine_desc->l2c_write_sec;

  • ret = l2x0_of_init(machine_desc->l2c_aux_val,

  • machine_desc->l2c_aux_mask);

  • if (ret && ret != -ENODEV)

  • pr_err("L2C: failed to init: %d\n", ret);

  • }

  • ?
  • uniphier_cache_init();

  • }

  • machine_desc->init_irq()?完成對(duì)中斷控制器的初始化,為每個(gè)irq_desc結(jié)構(gòu)安裝合適的流控handler,為每個(gè)irq_desc結(jié)構(gòu)安裝irq_chip指針,使他指向正確的中斷控制器所對(duì)應(yīng)的irq_chip結(jié)構(gòu)的實(shí)例,同時(shí),如果該平臺(tái)中的中斷線有多路復(fù)用(多個(gè)中斷公用一個(gè)irq中斷線)的情況,還應(yīng)該初始化irq_desc中相應(yīng)的字段和標(biāo)志,以便實(shí)現(xiàn)中斷控制器的級(jí)聯(lián)。

    這里初始化的時(shí)候回調(diào)用到具體的芯片相關(guān)的中斷初始化的地方。

    例如:

  • int __init s5p_init_irq_eint(void)

  • {

  • int irq;

  • ?
  • for (irq = IRQ_EINT(0); irq <= IRQ_EINT(15); irq++)

  • irq_set_chip(irq, &s5p_irq_vic_eint);

  • ?
  • for (irq = IRQ_EINT(16); irq <= IRQ_EINT(31); irq++) {

  • irq_set_chip_and_handler(irq, &s5p_irq_eint, handle_level_irq);

  • set_irq_flags(irq, IRQF_VALID);

  • }

  • ?
  • irq_set_chained_handler(IRQ_EINT16_31, s5p_irq_demux_eint16_31);

  • return 0;

  • }

  • 而在這些里面,都回去調(diào)用類似于:

  • void

  • irq_set_chip_and_handler_name(unsigned int irq, struct irq_chip *chip,

  • irq_flow_handler_t handle, const char *name);

  • ?
  • irq_set_handler(unsigned int irq, irq_flow_handler_t handle)

  • {

  • __irq_set_handler(irq, handle, 0, NULL);

  • }

  • ?
  • static inline void

  • irq_set_chained_handler(unsigned int irq, irq_flow_handler_t handle)

  • {

  • __irq_set_handler(irq, handle, 1, NULL);

  • }

  • ?
  • void

  • irq_set_chained_handler_and_data(unsigned int irq, irq_flow_handler_t handle,

  • void *data);

  • ?
  • 這些函數(shù)定義在 include/linux/irq.h 文件。是對(duì)芯片初始化的時(shí)候可見的 APIs,用于指定中斷“流控”中的 :

    irq_flow_handler_t handle

    也就是中斷來的時(shí)候,最后那個(gè)函數(shù)調(diào)用。

    中斷流控函數(shù),分幾種,電平觸發(fā)的中斷,邊沿觸發(fā)的,等:

  • /*

  • * Built-in IRQ handlers for various IRQ types,

  • * callable via desc->handle_irq()

  • */

  • extern void handle_level_irq(struct irq_desc *desc);

  • extern void handle_fasteoi_irq(struct irq_desc *desc);

  • extern void handle_edge_irq(struct irq_desc *desc);

  • extern void handle_edge_eoi_irq(struct irq_desc *desc);

  • extern void handle_simple_irq(struct irq_desc *desc);

  • extern void handle_untracked_irq(struct irq_desc *desc);

  • extern void handle_percpu_irq(struct irq_desc *desc);

  • extern void handle_percpu_devid_irq(struct irq_desc *desc);

  • extern void handle_bad_irq(struct irq_desc *desc);

  • extern void handle_nested_irq(unsigned int irq);

  • 而在這些處理函數(shù)里,會(huì)去調(diào)用到 :?handle_irq_event?

    比如:

  • /**

  • * handle_level_irq - Level type irq handler

  • * @desc: the interrupt description structure for this irq

  • *

  • * Level type interrupts are active as long as the hardware line has

  • * the active level. This may require to mask the interrupt and unmask

  • * it after the associated handler has acknowledged the device, so the

  • * interrupt line is back to inactive.

  • */

  • void handle_level_irq(struct irq_desc *desc)

  • {

  • raw_spin_lock(&desc->lock);

  • mask_ack_irq(desc);

  • ?
  • if (!irq_may_run(desc))

  • goto out_unlock;

  • ?
  • desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);

  • ?
  • /*

  • * If its disabled or no action available

  • * keep it masked and get out of here

  • */

  • if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {

  • desc->istate |= IRQS_PENDING;

  • goto out_unlock;

  • }

  • ?
  • kstat_incr_irqs_this_cpu(desc);

  • handle_irq_event(desc);

  • ?
  • cond_unmask_irq(desc);

  • ?
  • out_unlock:

  • raw_spin_unlock(&desc->lock);

  • }

  • 而這個(gè) handle_irq_event 則是調(diào)用了處理,handle_irq_event_percpu

  • irqreturn_t handle_irq_event(struct irq_desc *desc)

  • {

  • irqreturn_t ret;

  • ?
  • desc->istate &= ~IRQS_PENDING;

  • irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);

  • raw_spin_unlock(&desc->lock);

  • ?
  • ret = handle_irq_event_percpu(desc);

  • ?
  • raw_spin_lock(&desc->lock);

  • irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);

  • return ret;

  • }

  • handle_irq_event_percpu->__handle_irq_event_percpu-> 【action->handler()

    這里終于看到了調(diào)用 的地方了,就是咱們通過 request_irq 注冊(cè)進(jìn)去的函數(shù)


    ?

    7. /proc/interrupts

    這個(gè) proc 下放置了對(duì)應(yīng)中斷號(hào)的中斷次數(shù)和對(duì)應(yīng)的 dev-name

    《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

    總結(jié)

    以上是生活随笔為你收集整理的Linux 中断之中断处理浅析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。