Interrupt 架构之美 -- Linux Kernel 实现欣赏

这是以前的浅析 :
http://blog.csdn.net/leesagacious/article/details/50500096
http://blog.csdn.net/leesagacious/article/details/50491819
http://blog.csdn.net/leesagacious/article/details/78679903

注册中断

这里写图片描述

int request_threaded_irq(unsigned int irq, irq_handler_t handler,
             irq_handler_t thread_fn, unsigned long irqflags,
             const char *devname, void *dev_id)
{
    /*
        函数中一上来就声明的结构体,其地位都是非常重要的,
        作者一上来就想到、就要声明它,大都是该体系中的核心数据结构,
        如下的两个结构体蕴含丰富,用到了在说吧,
    */
    struct irqaction *action;
    struct irq_desc *desc;
    int retval;

    /*
        绝对不推荐使用共享中断,
        1 : SOC支持的中断源数量很多,如 : 256 - 32 = 224个 如果还不够你用, 那你可以选择级联的中断控制器的SOC了
        2 : 遍历共享中断处理函数形成的irqaction链表,执行每一个ISR
            查看各自的状态寄存器看是否是自己发生了中断,
            清中断(163 sts中断在读取该状态寄存器的时候,就被清理了,164 mo中断一定要手动清理(真是一个辣鸡!),否则中断风暴要来了
            参见上图)
            这延迟了中断处理的时间了吧,与"上半部分执行的越快越好"的理念是违背的
            为什么 “上半部分执行的越快越好”,这涉及到了上、下半部分诞生的原因了,下面说

        dev_id 是ISR向被中断的进程开了一扇窗,可传一个值,暴力的Interrupt也有温柔的一面,哈哈!    
        请不要再联想dev_id与共享中断的事情了,说什么free_irq()释放时唯一性的那些东西,因为不推荐共享中断,

        加上这个do....while() 是为了处理共享中断情况的,大多数情况下,只会执行一次,因为只有共享中断,链表上
        才会有多个irqaction,否则只有一个 

        请移步: http://blog.csdn.net/leesagacious/article/details/51884483

        do {
            res = action->handler(irq, action->dev_id);  你写的ISR被调用
            .....
            action = action->next;  下一个
        } while (action);
    */
    if ((irqflags & IRQF_SHARED) && !dev_id)
        return -EINVAL;

    /*
        从Radix Tree中 找到中断描述符 struct irq_desc
        上面声明的,下面肯定会有对应的响应的,念念不忘,必有回响,哈哈!
        可能会说,不是从数组中根据软件中断号irq对应的数组下标来找到的吗,
        以前的kernel确实是用数组来管理的,这又涉及到初始化的部分了,还是下面在说,

        关于是 "描述符"的东西 都绝对是核心的东西了,你可能又联想到USB的那些很多描述符了吧,
        那么 这里的 irq_desc 描述了哪些属性 ? 你想一下吧,它应该描述哪些东西 

        return radix_tree_lookup(&irq_desc_tree, irq);

        用基数树来管理比用数组来管理有哪些优势 ? 那个惯用的数组下标的经典手法又一次体现出来了
    */
    desc = irq_to_desc(irq);
    if (!desc)
       return -EINVAL;

    /*
       中断描述符是一种资源,预先分配好的,这个不像 进程的ID 使用延迟重用算法 和 文件描述符fd 那样的动态
       分配的了,
       有些是不可以用的,       
       源码 : 
            return !(desc->status_use_accessors & _IRQ_NOREQUEST);
            return desc->status_use_accessors & _IRQ_PER_CPU_DEVID;

       irq_desc是怎么被初始化的,下面说,这里先把这个函数流程执行完成
    */
    if (!irq_settings_can_request(desc) || WARN_ON(irq_settings_is_per_cpu_devid(desc)))
        return -EINVAL;

    /*
        handler你不给,thread_fn你也不给,你让Interrupt做什么呢?
        如果鱼与熊掌偏偏都给你了,下面看看Interrupt是怎么做的 ?
        好,这里就是 该函数的核心的地方了 !
    */
    if (!handler) {
        if (!thread_fn)
            return -EINVAL;
        /*
            return IRQ_WAKE_THREAD;
            就一句话,它要唤醒中断线程.
            那么要问了,
            1 : 中断线程是在什么时机被二号进程 kthreadd 产生的 ?
            2 : 又是再什么时机被唤醒的

            多说一句,内核线程大都是这个样子,一上来就睡,对应的条件出发了,再去唤醒它,
            就像Hub的驱动 khubd内核线程一样,hub上的port被接入了usb设备后,它就被唤醒了
            来枚举Port上的设备了,
            但是,这个内核线程来头可不一般 !
        */  
        handler = irq_default_primary_handler;
    }

    /*
        分配内存空间吧,这里就是 irqaction的起点了.
        终点在哪 ? 当然是 free_irq()了,有分配,必有释放,哈哈!
    */
    action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
    if (!action)
        return -ENOMEM;
    /*
        利用传入的参数 给它赋值,然后挂入链表
        这里想到了signal挂入链表的情景,但是,这里的是单链表,
        且在执行的时候,无需查找链表节点的,而是一次全部触发(共享中断)

        看,上面是一大串校验,这里才开始实际的工作.
    */
    action->handler = handler;
    action->thread_fn = thread_fn;
    action->flags = irqflags;      // 关注这个 flags这个值,做一个标记.
    action->name = devname;
    action->dev_id = dev_id;

    chip_bus_lock(desc);
    /*
        主角 终于要粉墨登场了!
    */
    retval = __setup_irq(irq, desc, action);
    chip_bus_sync_unlock(desc);

    if (retval)
        kfree(action);
}
/**
    这个函数很长,做的事情很多
    @irq       : 软件中断号, 
    @desc      : 中断源描述符    
    @irqaction : 中断动作描述符 ,既然是动作描述符,那么一定会有pointer指向你写的ISR.
*/
static int __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
{
    struct irqaction *old, **old_ptr;
    unsigned long flags, thread_mask = 0;
    int ret, nested, shared = 0;
    cpumask_var_t mask;
}

猜你喜欢

转载自blog.csdn.net/leesagacious/article/details/78876848