《Linux内核设计与实现》学习【4】—— 中断

    科技2022-07-14  156

    1 上/下半部

    将中断处理程序分为两部分:   1)上半部:做严格时限的工作   2)下半部:允许稍后执行的工作

    2 使用

    注册

    /* * irg - 表示要分配的中断号 * handler - 实际的中断处理程序 * flags - 标志位,表示此中断的具有特性 * name - 中断设备名称的ASCII 表示,这些会被/proc/irq和/proc/interrupts文件使用 * dev - 用于共享中断线,多个中断程序共享一个中断线时(共用一个中断号),依靠dev来区别各个中断程序 * 返回值: * 执行成功:0 * 执行失败:非0 */ int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char* name, void *dev)

    释放

    void free_irq(unsigned int irq, void *dev)

    3 上半部流程

    4 下半部

      下半部的3种机制:软中断、tasklet和工作队列。

    4.1 软中断

      在编译期间静态分配,不能动态注册或注销   软中断由softirq_action表示。

    struct softirq_action { void (*action)(struct softirq_action *); };

      在kernel/softirq.c中,维护了softirq_action的数组

    static struct softirq_action softirq_vec[NR_SOFTIRQS];

      在include/linux/interrupt.h中当前内核所使用的软中断

    enum { HI_SOFTIRQ=0, TIMER_SOFTIRQ, NET_TX_SOFTIRQ, NET_RX_SOFTIRQ, BLOCK_SOFTIRQ, BLOCK_IOPOLL_SOFTIRQ, TASKLET_SOFTIRQ, SCHED_SOFTIRQ, HRTIMER_SOFTIRQ, RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */ NR_SOFTIRQS };

      注册软中断,设置action成员的处理函数

    void open_softirq(int nr, void (*action)(struct softirq_action *)) { softirq_vec[nr].action = action; }

      触发软中断

    void raise_softirq(unsigned int nr)

      之后将在合适的时间执行,在下列地方检查和执行:   1)从一个硬中断返回时   2)在ksoftirqd内核线程中被执行   3)在那些显式检查或执行待处理软中断的代码中,如网络子系统中   最终都要在do_softirq()中执行

    asmlinkage void do_softirq(void) { __u32 pending; unsigned long flags; if (in_interrupt())//在中断上下文中,或者是在禁止了软中断后调用了 return; local_irq_save(flags); pending = local_softirq_pending();//待处理软中断的位图 if (pending) __do_softirq(); local_irq_restore(flags); } asmlinkage void __do_softirq(void) { struct softirq_action *h; ... pending = local_softirq_pending(); ... h = softirq_vec; do { if (pending & 1) {//对应标志位被设置了 int prev_count = preempt_count(); kstat_incr_softirqs_this_cpu(h - softirq_vec); trace_softirq_entry(h, softirq_vec); h->action(h); //执行action trace_softirq_exit(h, softirq_vec); if (unlikely(prev_count != preempt_count())) { printk(KERN_ERR "huh, entered softirq %td %s %p" "with preempt_count x," " exited with x?\n", h - softirq_vec, softirq_to_name[h - softirq_vec], h->action, prev_count, preempt_count()); preempt_count() = prev_count; } rcu_bh_qs(cpu); } h++; pending >>= 1; } while (pending); ... }

    4.2 tasklet

      通过软中断实现,两类:HI_SOFTIRQ和TASKLET_SOFTIRQ   初始化时,start_kernel()–>softirq_init

    void __init softirq_init(void) { ... open_softirq(TASKLET_SOFTIRQ, tasklet_action); open_softirq(HI_SOFTIRQ, tasklet_hi_action); }

      tasklet由tasklet_struct结构体表示

    struct tasklet_struct { struct tasklet_struct *next; /* 链表中的下一个tasklet */ unsigned long state; /* tasklet状态 */ atomic_t count; /* 引用计数器 */ void (*func)(unsigned long); /* tasklet处理函数 */ unsigned long data; /* tasklet处理函数的参数 */ };

      已调度的tasklet存放在单处理器数据结构:tasklet_vec和tasklet_hi_vec   调度:tasklet_schedule()和tasklet_hi_schedule()   tasklet_schedule()–>__tasklet_schedule()–>raise_softirq_irqoff(TASKLET_SOFTIRQ),唤起对应的软中断,之后在do_softirq()中调用对应的action,即tasklet_action

    static void tasklet_action(struct softirq_action *a) { struct tasklet_struct *list; local_irq_disable(); //禁止中断 list = __get_cpu_var(tasklet_vec).head; __get_cpu_var(tasklet_vec).head = NULL;//清空,防止SMP时影响 __get_cpu_var(tasklet_vec).tail = &__get_cpu_var(tasklet_vec).head; local_irq_enable(); while (list) { //遍历 struct tasklet_struct *t = list; list = list->next; if (tasklet_trylock(t)) { //检查并设置TASKLET_STATE_RUN if (!atomic_read(&t->count)) {//检查是否被禁止 if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state)) BUG(); t->func(t->data);//执行对应的func tasklet_unlock(t); continue; } tasklet_unlock(t); } local_irq_disable(); t->next = NULL; *__get_cpu_var(tasklet_vec).tail = t; __get_cpu_var(tasklet_vec).tail = &(t->next); __raise_softirq_irqoff(TASKLET_SOFTIRQ); local_irq_enable(); } }

    4.3 工作队列

      把工作交由内核线程取执行,允许重新调度、睡眠。   相关数据结构

    struct work_struct { atomic_long_t data; /* 这个并不是处理函数的参数,而是表示此work是否pending等状态的flag */ #define WORK_STRUCT_PENDING 0 /* T if work item pending execution */ #define WORK_STRUCT_FLAG_MASK (3UL) #define WORK_STRUCT_WQ_DATA_MASK (~WORK_STRUCT_FLAG_MASK) struct list_head entry; /* 中断下半部处理函数的链表 */ work_func_t func; /* 处理中断下半部工作的函数 */ #ifdef CONFIG_LOCKDEP struct lockdep_map lockdep_map; #endif }; struct cpu_workqueue_struct { spinlock_t lock; /* 锁保护这种结构 */ struct list_head worklist; /* 工作队列头节点 */ wait_queue_head_t more_work; struct work_struct *current_work; struct workqueue_struct *wq; /* 关联工作队列结构 */ struct task_struct *thread; /* 关联线程 */ } ____cacheline_aligned; struct workqueue_struct { struct cpu_workqueue_struct *cpu_wq; /* 工作者线程 */ struct list_head list; const char *name; int singlethread; int freezeable; /* Freeze threads during suspend */ int rt; #ifdef CONFIG_LOCKDEP struct lockdep_map lockdep_map; #endif };

      workqueue_struct:工作者线程workqueue_struct用表示   cpu_workqueue_struct:每个处理器对应的工作者线程表示   work_struct:工作处理,组成链表   关系

    4.4 总结

    软中断tasklet工作队列上下文中断中断进程顺序执行保障无同类型不能同时执行无

    选择时考虑:   1)是否需要休眠   2)性能

    Processed: 0.018, SQL: 8