内核代码阅读(15) - 中断请求队列的初始化

简介: 中断请求队列的初始化

中断请求队列的初始化

数据结构

irq通道的控制

struct hw_interrupt_type {
        const char * typename;
        unsigned int (*startup)(unsigned int irq);
        void (*shutdown)(unsigned int irq);
        void (*enable)(unsigned int irq);
        void (*disable)(unsigned int irq);
        void (*ack)(unsigned int irq);
        void (*end)(unsigned int irq);
        void (*set_affinity)(unsigned int irq, unsigned long mask);
    };
    typedef struct hw_interrupt_type  hw_irq_controller;
1) startup
   启动通道
2) ack
   响应

irq请求队列头部数据结构

typedef struct {
        unsigned int status;                /* IRQ status */
        hw_irq_controller *handler;
        struct irqaction *action;        /* IRQ action list */
        unsigned int depth;                /* nested irq disables */
        spinlock_t lock;
    } ____cacheline_aligned irq_desc_t;
    extern irq_desc_t irq_desc [NR_IRQS];
1) handler 就是通道级别的操作函数。
2) action 是这个队列的头部,类型是 irqaction。

irqaction结构

struct irqaction {
        void (*handler)(int, void *, struct pt_regs *);
        unsigned long flags;
        unsigned long mask;
        const char *name;
        void *dev_id;
        struct irqaction *next;
    };
1) handler
   就是中断服务程序。
2) dev_id
   是设备号,因为这个通道是共享的,所以要依赖这个dev_id区分到底是谁产生的中断。
3) next 链表。

队列头irq_desc的初始化

在 init_IRQ -> init_ISA_irqs中
void __init init_ISA_irqs (void)
    {
        int i;
        init_8259A(0);
        for (i = 0; i < NR_IRQS; i++) {
                irq_desc[i].status = IRQ_DISABLED;
                irq_desc[i].action = 0;
                irq_desc[i].depth = 1;
                if (i < 16) {
                        irq_desc[i].handler = &i8259A_irq_type;
                } else {
                        irq_desc[i].handler = &no_irq_type;
                }
        }
    }
1) irq_desc[i].handler = &i8259A_irq_type;
   设置队列头 irq_desc中的handler为8259的处理函数。

添加一个 interrupt line 到系统中(外设主动调用把自己的中断服务程序注册到相对的请求队列中)

int request_irq(unsigned int irq, 
                void (*handler)(int, void *, struct pt_regs *),
                unsigned long irqflags, 
                const char * devname,
                void *dev_id)
    {
        int retval;
        struct irqaction * action;
    #if 1
        if (irqflags & SA_SHIRQ) {
                if (!dev_id)
                        printk("Bad boy: %s (at 0x%x) called us without a dev_id!\n", devname, (&irq)[-1]);
        }
    #endif
        if (irq >= NR_IRQS)
                return -EINVAL;
        if (!handler)
                return -EINVAL;
        action = (struct irqaction *)
                        kmalloc(sizeof(struct irqaction), GFP_KERNEL);
        if (!action)
                return -ENOMEM;
        action->handler = handler;
        action->flags = irqflags;
        action->mask = 0;
        action->name = devname;
        action->next = NULL;
        action->dev_id = dev_id;
        retval = setup_irq(irq, action);
        if (retval)
                kfree(action);
        return retval;
    }
1) if (!dev_id)
   如果没有dev_id则报错。
2) kmalloc(sizeof(struct irqaction), GFP_KERNEL);
   从slab分配一个 irqaction
3) action->handler = handler;
   设置handler
4) action->dev_id = dev_id;
   设置dev_id
5) printk("Bad boy: %s (at 0x%x) called us without a dev_id!\n", devname, (&irq)[-1]);
   注意这个printk的技巧:打印出调用者的指令地址。
   &irq取出第一个参数在栈上的地址,
   (&irq)[-1] 在栈上往上找一个,就是函数的返回地址。也就是调用者的地址。

setup_irq 链入请求队列

int setup_irq(unsigned int irq, struct irqaction * new)
    {
        int shared = 0;
        unsigned long flags;
        struct irqaction *old, **p;
        irq_desc_t *desc = irq_desc + irq;
        if (new->flags & SA_SAMPLE_RANDOM) {
                rand_initialize_irq(irq);
        }
        spin_lock_irqsave(&desc->lock,flags);
        p = &desc->action;
        if ((old = *p) != NULL) {
                if (!(old->flags & new->flags & SA_SHIRQ)) {
                        spin_unlock_irqrestore(&desc->lock,flags);
                        return -EBUSY;
                }
                do {
                        p = &old->next;
                        old = *p;
                } while (old);
                shared = 1;
        }
        *p = new;
        if (!shared) {
                desc->depth = 0;
                desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING);
                desc->handler->startup(irq);
        }
        spin_unlock_irqrestore(&desc->lock,flags);
        register_irq_proc(irq);
        return 0;
    }
1) rand_initialize_irq(irq);
   借助外设中断的时序来增进随机数。
2) do {
            p = &old->next;
            old = *p;
  } while (old);
  找到队列尾端,并插入。
相关文章
|
22天前
|
调度 项目管理
内核初始化的过程
【9月更文挑战第13天】内核启动始于函数 `start_kernel()`,该函数位于 `init/main.c` 文件中,包含各类初始化函数。操作系统首先创建 0 号进程 `init_task`,随后初始化中断处理、内存管理、调度模块及虚拟文件系统 VFS。
|
5月前
|
缓存 Linux C语言
Linux线程的创建过程
【2月更文挑战第10天】
|
5月前
|
消息中间件 调度 数据安全/隐私保护
xenomai内核解析--任务同步互斥机制(一)--优先级倒置
本文是关于Xenomai实时操作系统中资源管理和优先级倒置问题的概述。Xenomai使用`xnobject`和`xnregistry`管理任务间的同步互斥资源,如信号量、互斥锁等。资源管理涉及访问控制和资源保存,确保共享资源的正确调度。文章还介绍了优先级倒置现象,即高优先级任务因低优先级任务持有资源而被阻塞。为解决此问题,Xenomai采用了优先级继承策略,临时提升低优先级任务的优先级,以防止持续的优先级反转。文章后续将深入分析`xnsynch`模块和优先级倒置解决方案。
128 1
xenomai内核解析--任务同步互斥机制(一)--优先级倒置
|
算法 C语言
如何在C语言中实现队列和堆栈的动态扩容
队列和堆栈是在C语言中常用的数据结构,它们可以帮助我们高效地处理数据。然而,在实际编程中,我们经常会遇到数据量超过容量限制的情况。这时,我们需要实现队列和堆栈的动态扩容,以满足实际需求。
107 1
|
5月前
|
Java 程序员 API
【深入探究 Qt 线程】一文详细解析Qt线程的内部原理与实现策略
【深入探究 Qt 线程】一文详细解析Qt线程的内部原理与实现策略
480 0
|
10月前
|
监控 安全 API
6.9 Windows驱动开发:内核枚举进线程ObCall回调
在笔者上一篇文章`《内核枚举Registry注册表回调》`中我们通过特征码定位实现了对注册表回调的枚举,本篇文章`LyShark`将教大家如何枚举系统中的`ProcessObCall`进程回调以及`ThreadObCall`线程回调,之所以放在一起来讲解是因为这两中回调在枚举是都需要使用通用结构体`_OB_CALLBACK`以及`_OBJECT_TYPE`所以放在一起来讲解最好不过。
130 1
6.9 Windows驱动开发:内核枚举进线程ObCall回调
|
5月前
|
存储 调度
FreeRTOS深入教程(队列内部机制和源码分析)
FreeRTOS深入教程(队列内部机制和源码分析)
113 0
驱动开发:内核中枚举进线程与模块
内核枚举进程使用`PspCidTable` 这个未公开的函数,它能最大的好处是能得到进程的EPROCESS地址,由于是未公开的函数,所以我们需要变相的调用这个函数,通过`PsLookupProcessByProcessId`函数查到进程的EPROCESS,如果`PsLookupProcessByProcessId`返回失败,则证明此进程不存在,如果返回成功则把EPROCESS、PID、PPID、进程名等通过DbgPrint打印到屏幕上。
454 0
驱动开发:内核中枚举进线程与模块
驱动开发:内核遍历进程VAD结构体
在上一篇文章`《驱动开发:内核中实现Dump进程转储》`中我们实现了ARK工具的转存功能,本篇文章继续以内存为出发点介绍`VAD`结构,该结构的全程是`Virtual Address Descriptor`即`虚拟地址描述符`,VAD是一个`AVL`自`平衡二叉树`,树的每一个节点代表一段虚拟地址空间。程序中的代码段,数据段,堆段都会各种占用一个或多个`VAD`节点,由一个`MMVAD`结构完整描述。
468 0
驱动开发:内核遍历进程VAD结构体
|
缓存 Linux C语言
库函数与系统调用之间的区别--扩展知识点1
库函数与系统调用之间的区别--扩展知识点1
188 0