1. Linux中断分层

(1)上半部:当中断发生时,它进行相应的硬件读写,并“登记”该中断。通常由中断处理程序充当上半部。(一般情况下,上半部不可被打断)

(2)下半部:在系统空闲的时候,对上半部“登记”的中断进行后续处理(“延迟处理”)

2. 对于中断下半部的实现方式一共有三种

(1)软中断

(2)tasklet微线程

(3)工作队列

3. Linux内核软中断分析

(1)当中断发生时,Linux内核会跳转到中断总入口函数asm_do_IRQ(),根据传入的中断号,执行相应handle_irq()函数。做完这些工作之后,会调用函数irq_exit()(位于文件:kernal/softirq.c),该函数负责调用和处理待决的软中断。

  1. void irq_exit(void)
  2. {
  3. account_system_vtime(current);
  4. trace_hardirq_exit();
  5. sub_preempt_count(IRQ_EXIT_OFFSET);
  6. if (!in_interrupt() && local_softirq_pending())
  7. invoke_softirq();
  8.  
  9. #ifdef CONFIG_NO_HZ
  10. /* Make sure that timer wheel updates are propagated */
  11. rcu_irq_exit();
  12. if (idle_cpu(smp_processor_id()) && !in_interrupt() && !need_resched())
  13. tick_nohz_stop_sched_tick();
  14. #endif
  15. preempt_enable_no_resched();
  16. }

(2)invoke_softirq()是一个宏,等价于do_softirq()。调用do_softirq函数,就说明程序已经进入软中断环境了。与asm_do_IRQ所处的中断的上半部不同,处于软中断环境中,是可以被其他中断程序打断的,甚至是处于同一中断线的中断。也因为此,所以软中断可以执行一些稍微时间长一点的任务,也不会迟滞系统对中断的反应时间。
(3)软中断由一个softirq_action结构体表示,在该结构体中只定义了一个函数指针

  1. struct softirq_action
  2. {
  3. void (*action)(struct softirq_action *);
  4. };

(4)Linux内核中用一个数据项为softirq_action类型的数组softirq_vec来存储所支持的所有软中断

  1. static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;

(5)在驱动程序中,注册软中断使用函数open_softirq

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

(6)softirq_vec数组的下表代表不同类型的软中断,值越小,优先级越高,他们定义为一个枚举常量

  1. enum
  2. {
  3. HI_SOFTIRQ=,
  4. TIMER_SOFTIRQ,
  5. NET_TX_SOFTIRQ,
  6. NET_RX_SOFTIRQ,
  7. BLOCK_SOFTIRQ,
  8. TASKLET_SOFTIRQ,
  9. SCHED_SOFTIRQ,
  10. HRTIMER_SOFTIRQ,
  11. RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */
  12.  
  13. NR_SOFTIRQS
  14. };

(7)驱动程序所要做的工作是把软中断注册进去,而执行是do_softirq调用一个名为Ksoftirq的内核线程来完成。我们如果想要执行我们注册的软中断,还需要调用raise_softirq函数将我们想要执行的软中断处理程序挂起

  1. void raise_softirq(unsigned int nr)
  2. {
  3. unsigned long flags;
  4.  
  5. local_irq_save(flags);
  6. raise_softirq_irqoff(nr);
  7. local_irq_restore(flags);
  8. }

(8)相应类型的软中断被挂起之后,将会通过do_softirq得到执行

  1. asmlinkage void do_softirq(void)
  2. {
  3. __u32 pending;
  4. unsigned long flags;
  5.  
  6. if (in_interrupt())
  7. return;
  8.  
  9. local_irq_save(flags);
  10.  
  11. pending = local_softirq_pending();
  12.  
  13. if (pending)
  14. __do_softirq();
  15.  
  16. local_irq_restore(flags);
  17. }

(9)do_softirq调用函数__do_softirq来真正执行软中断

  1. asmlinkage void __do_softirq(void)
  2. {
  3. struct softirq_action *h;
  4. __u32 pending;
  5. int max_restart = MAX_SOFTIRQ_RESTART;
  6. int cpu;
  7.  
  8. pending = local_softirq_pending();
  9. account_system_vtime(current);
  10.  
  11. __local_bh_disable((unsigned long)__builtin_return_address());
  12. trace_softirq_enter();
  13.  
  14. cpu = smp_processor_id();
  15. restart:
  16. /* Reset the pending bitmask before enabling irqs */
  17. set_softirq_pending();
  18.  
  19. local_irq_enable();
  20.  
  21. h = softirq_vec;
  22.  
  23. do {
  24. if (pending & ) {
  25. int prev_count = preempt_count();
  26.  
  27. h->action(h);
  28.  
  29. if (unlikely(prev_count != preempt_count())) {
  30. printk(KERN_ERR "huh, entered softirq %td %p"
  31. "with preempt_count %08x,"
  32. " exited with %08x?\n", h - softirq_vec,
  33. h->action, prev_count, preempt_count());
  34. preempt_count() = prev_count;
  35. }
  36.  
  37. rcu_bh_qsctr_inc(cpu);
  38. }
  39. h++;
  40. pending >>= ;
  41. } while (pending);
  42.  
  43. local_irq_disable();
  44.  
  45. pending = local_softirq_pending();
  46. if (pending && --max_restart)
  47. goto restart;
  48.  
  49. if (pending)
  50. wakeup_softirqd();
  51.  
  52. trace_softirq_exit();
  53.  
  54. account_system_vtime(current);
  55. _local_bh_enable();
  56. }

4. Linux内核tasklet分析

(1)软中断是将操作推迟到将来某一个时刻执行的最有效的方法。由于该延迟机制处理复杂,多个处理器可以同时并且独立得处理(即do_softirq函数可以被多个CPU同时执行),并且一个软中断的处理程序可以在多个CPU上同时执行,因此处理程序必须要被设计为完全可重入和线程安全的。此外临界区必须用自旋锁保护。软中断因为这些原因显得太过于麻烦,因此引入tasklet机制。

(2)tasklet是基于软中断实现的,确切的说应该是软中断的一个类型。所以根据软中断的性质,一个软中断类型对应一个软中断处理程序action。同理,也可以推出tasklet也会对应于一个唯一的action。

(3)每一个CPU都会有自己独立的tasklet队列,虽然一个tasklet类型的软中断只对应一个action处理程序,但是我们可以在该处理程序中轮询执行一个tasklet队列,队列里面的每一个tasklet_struct都会对应一个tasklet处理函数,这样当我们的驱动程序中需要使用到tasklet的时候,只要往这个tasklet队列加入我们自定义的tasklet_struct对象就可以了。同时,由于每一个CPU都会有一个tasklet队列,并且每一个CPU只会执行自己tasklet队列里面的tasklet_struct对象,因此tasklet并不需要自旋锁的保护(当然这只能是对同一个tasklet而言,如果多个不同的tasklet需要使用同一资源的话,仍需要自旋锁的保护)。

(4)Linux内核通过一个tasklet_struct结构体来描述一个tasklet对象,该结构体定义在include\linux\interrupt.h文件中

  1. struct tasklet_struct
  2. {
  3. struct tasklet_struct *next;
  4. unsigned long state;
  5. atomic_t count;
  6. void (*func)(unsigned long);
  7. unsigned long data;
  8. };

(5)tasklet的队列示意图

(5)Linux内核时通过名为tasklet_vec的tasklet_head结构体来组织tasklet对象的

  1. struct tasklet_head
  2. {
  3. struct tasklet_struct *head;
  4. struct tasklet_struct **tail;
  5. };

  head:指向第一个tasklet_struct结构体的指针

  tail:指向tasklet队列最后一个tasklet_struct的next指针的地址

(6)如果tasklet队列没有元素,两个指针的指向是这样的

(7)向tasklet队列添加tasklet_struct对象,就是将最后一个tasklet_sturct的next指针指向新加的tasklet_struct对象,同时将列表头的tail指针的指针指向新加的tasklet_struct结构体的next指针的地址。代码如下

  1. tasklet_struct * t;
  2. * _get_cpu_var(tasklet_vec).tail = t;
  3. _get_cpu_var(tasklet_vec).tail = &(t->next);

(8)tasklet类型的软中断唯一对应的action叫做tasklet_action(一般其他类型软中断的action都是由用户自己编写,但是tasklet不一样,Linux设计师已经帮我们实现了。所以也是因为这样,tasklet被广泛应用于驱动程序中)

  1. static void tasklet_action(struct softirq_action *a)
  2. {
  3. struct tasklet_struct *list;
  4.  
  5. local_irq_disable();
  6. list = __get_cpu_var(tasklet_vec).head;
  7. __get_cpu_var(tasklet_vec).head = NULL;
  8. __get_cpu_var(tasklet_vec).tail = &__get_cpu_var(tasklet_vec).head;
  9. local_irq_enable();
  10.  
  11. while (list) {
  12. struct tasklet_struct *t = list;
  13.  
  14. list = list->next;
  15.  
  16. if (tasklet_trylock(t)) {
  17. if (!atomic_read(&t->count)) {
  18. if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
  19. BUG();
  20. t->func(t->data);
  21. tasklet_unlock(t);
  22. continue;
  23. }
  24. tasklet_unlock(t);
  25. }
  26.  
  27. local_irq_disable();
  28. t->next = NULL;
  29. *__get_cpu_var(tasklet_vec).tail = t;
  30. __get_cpu_var(tasklet_vec).tail = &(t->next);
  31. __raise_softirq_irqoff(TASKLET_SOFTIRQ);
  32. local_irq_enable();
  33. }
  34. }

(9)另一个tasklet非常重要的函数,就是tasklet_schedule,这个函数通常用于中断处理程序中,用于将tasklet_struct加入所在CPU的tasklet队列,同时将tasklet软中断挂起。
因为我们知道,在中断的上半部中的irq_exit函数中,会激活do_softirq函数,所以在中断处理程序中使用tasklet_schedule函数就显得特别必要。tasklet_schedule源代码如下,

  1. static inline void tasklet_schedule(struct tasklet_struct *t)
  2. {
  3. if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
  4. __tasklet_schedule(t);
  5. }

test_and_set_bit(TASKLET_STATE_SCHED, &t->state):这个函数的目的是设置t->state的第TASKLET_STATE_SCHED(0)位,并返回t->state的第TASKLET_STATE_SCHED位原来的值。

(10)_tasklet_schedule函数:

  1. void __tasklet_schedule(struct tasklet_struct *t)
  2. {
  3. unsigned long flags;
  4.  
  5. local_irq_save(flags);
  6. t->next = NULL;
  7. *__get_cpu_var(tasklet_vec).tail = t;
  8. __get_cpu_var(tasklet_vec).tail = &(t->next);
  9. raise_softirq_irqoff(TASKLET_SOFTIRQ);
  10. local_irq_restore(flags);
  11. }

(11)我们驱动程序中若要使用tasklet,首先我们还必须要创建一个tasklet_struct对象,通常创建tasklet_struct对象一共有两种方式:
① 静态方式:

  1. #define DECLARE_TASKLET(name, func, data) \
  2. struct tasklet_struct name = { NULL, , ATOMIC_INIT(), func, data }
  3.  
  4. #define DECLARE_TASKLET_DISABLED(name, func, data) \
  5. struct tasklet_struct name = { NULL, , ATOMIC_INIT(), func, data }

② 动态方式:

  1. static struct tasklet_struct my_tasklet;
  2. tasklet_init(&my_tasklet tasklet_handler, ); //count = 0,处于激活状态。

tasklet_init源码

  1. void tasklet_init(struct tasklet_struct *t,
  2. void (*func)(unsigned long), unsigned long data)
  3. {
  4. t->next = NULL;
  5. t->state = ;
  6. atomic_set(&t->count, );
  7. t->func = func;
  8. t->data = data;
  9. }

然后,我们再调用tasklet_schedule函数将tasklet对象加入到tasklet队列中即可。

Linux中断分层--软中断和tasklet的更多相关文章

  1. Linux中断分层技术

    一.中断嵌套  当系统正在执行某中断处理函数时,又产生了一个新的中断,这就叫做中断嵌套.当中断为慢速中断时,新的中断会取代当前中断,即当前中断没有执行完就结束 了:当中断为快速中断时,新的终端就不会产 ...

  2. Linux中断分层--工作队列

    1. 工作队列是一种将任务推后执行的方式,它把推后的任务交由一个内核线程去执行.这样中断的下半部会在进程上下文执行,他允许重新调度甚至睡眠.每个被推后的任务叫做“工作”,由这些工作组成的队列称为工作队 ...

  3. linux内核--软中断与tasklet

    硬件中断通常都需要在最短的时间内执行完毕,如果将所有硬件中断相关的处理都放在硬件中断处理程序中,那么就达不到这个目的. 通过linux提供的软中断和tasklet,可以将硬件中断处理程序中可以延迟处理 ...

  4. Linux中断底半部机制

    参考: Linux下半部处理之软中断 linux中断底半部机制 <深入理解Linux内核>软中断/tasklet/工作队列 软中断和tasklet介绍 详解操作系统中断 Linux内核:中 ...

  5. Linux中断管理 (2)软中断和tasklet

    目录: <Linux中断管理> <Linux中断管理 (1)Linux中断管理机制> <Linux中断管理 (2)软中断和tasklet> <Linux中断管 ...

  6. linux中断源码分析 - 软中断(四)

    本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 在上一篇文章中,我们看到中断实际分为了两个部分,俗称就是一部分是硬中断,一部分是软中断.软中断是专门用于处理中断 ...

  7. Linux内核中的软中断、tasklet和工作队列具体解释

    [TOC] 本文基于Linux2.6.32内核版本号. 引言 软中断.tasklet和工作队列并非Linux内核中一直存在的机制,而是由更早版本号的内核中的"下半部"(bottom ...

  8. Linux软中断、tasklet和工作队列

    Linux内核中的软中断.tasklet和工作队列详解 引言 软中断.tasklet和工作队列并不是Linux内核中一直存在的机制,而是由更早版本的内核中的“下半部”(bottom half)演变而来 ...

  9. Linux中断 - tasklet

    一.前言 对于中断处理而言,linux将其分成了两个部分,一个叫做中断handler(top half),属于不那么紧急需要处理的事情被推迟执行,我们称之deferable task,或者叫做bott ...

随机推荐

  1. Oracle——SQL基础

    一.SQL语句分为以下三种类型: DML: Data Manipulation Language 数据操纵语言DDL: Data Definition Language 数据定义语言DCL: Data ...

  2. 手机APP兼容性测试

    兼容性测试方案 兼容性问题 屏幕分辨率兼容性问题 软件(iOS和Android系统版本及不同厂家的定制ROM)兼容性问题 硬件(不同的CPU.内存大小等等)兼容性问题 网络(2G/3G/4G/WIFI ...

  3. window7 Oracle卸载步骤

    完全卸载oracle11g步骤:1. 开始->设置->控制面板->管理工具->服务(或 运行 services.msc) 停止所有Oracle服务.2. 开始->程序-& ...

  4. Robot Framework 使用总结

    最近项目上使用了RF快速实现了一些验收测试的自动化case,感觉不错,很好用,下面就记录一下使用RF实现自动化的过程. 什么是RF? RF是一种测试框架,帮助测试人员在其框架下快速实现验收测试的自动化 ...

  5. select2的搜索框不能输入搜索内容

    按照select2官网配置完后,搜索框弹出后无法输入内容,究竟怎么回事,于是在其他页面尝试了select2,发现可以啊,为什么在这个地方不可以,终于找到了造成这个问题的不同之处:select2在模态对 ...

  6. CentOS 网络操作

    ifconfig:查看网卡信息 网卡配置文件位置: /etc/sysconfig/network-scripts/文件夹 nmtui:配置网卡 netstat -tlunp:查看端口信息 端口信息存储 ...

  7. angular 子路由

    const routes: Routes = [ { path: '', redirectTo: '/home', pathMatch: 'full' }, { path: 'home', compo ...

  8. Android日期时间选择器DatePicker、TimePicker日期时间改变事件响应(Android学习笔记)

    activity_main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android&qu ...

  9. IDEA的一些常用快捷键以及配置

    IDEA常用快捷键:   保存:ctrl + s 关闭当前文件:ctrl + F4 撤销:ctrl + z 反撤销:ctrl + shift + z 查看方法实现类:ctrl + alt + B 移动 ...

  10. 基于stor2RRD 的 SAN、存储监控

    一. 配置用法在官网都有的详见网页: http://www.stor2rrd.com/install.htm?1.2 二 . 在这里我只是想记录一下我是如何编译安装Apache的,避免踩坑: 安装包如 ...