您的位置:首页 > 运维架构 > 网站架构

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

2017-12-22 21:11 309 查看
这是以前的浅析 :

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: