Linux内核实现中断和中断处理(二)

上回说了Linux内核实现中断会把中断分为两部分进行处理,上回讲了上部分,这回讲下部分的设计思路

  • 下半部的实现机制
    • 软中断
    • tasklet:是通过软中断实现的,但和软中断有所不同
    • 工作队列

讲上面几个实现机制之前先讲一个古老的方法,现在版本的内核虽然已经不再食用了,但是思想还在继续使用

最早的Linux只提供了“bottom half”这种机制实现下半部分,被称为BH,实现简单粗暴,设置一个全局变量(32位整数),表示一个32个节点的链表队列,哪位设置为1证明哪个bottom half就可以执行了。

  • 软中断

第一个先将软中断实现下半部分机制,要想将这个机制,必须得先说明软中断的实现方式,软中断实在编译期间静态分配的,kernel/softirq.c中定义了一个包含有32个结构体的数组static struct softirq_action softirq_vec[NR_SOFTIRQS],并且有一个对应的32位整数u32 pending,用来表示每个软中断的状态(不要嫌少,一般根本用不了那么多,一般9个10个就够用了,为什么这么少?很少有用软中断处理下半部分的,能用tasklet的地方绝不会使用软中断)

把软中断放进刚才说的32个长度的结构体数组中就完成了软中断的注册,想要执行软中断必须先标记注册好的软中断,这个过程被称为触发软中断,通常,中断处理程序(就是上半部分)会在返回之前标记它的软中断,所以不必担心,然后在合适的时刻就会执行该软中断

合适的时刻:1.从一个硬件中断代码处返回时;2.在ksoftirqd内核线程中;3.在那些显示检查和执行待处理的软中断的代码中;

不管是上面哪个时刻,软中断最终都是会被执行的,调用do_softirq()该函数会循环遍历(循环检查pending的每一个位,所以循环最多只能执行32次)

  • tasklet

因为takslet是使用软中断实现的,所以tasklet本身就是个软中断,我们是通过tasklet来实现下半部的机制的,所以在处理方式上和软中断十分的相似,tasklet由tasklet结构体表示,每一个结构体单独代表一个tasklet,它的定义如下

1 struct tasklet_struct
2 {
3      stauct tasklet_struct *next;//链表中的下一个节点
4      unsigned long state;//tasklet的状态
5      atomic_t count;//引用计数器
6      void (*func)(unsigned long);//tasklet处理函数
7      unsigned long data;//给tasklet处理函数的参数
8 }

其中tasklet的状态一共只有三种:0,TASKLET_STATE_SCHED,TASKLET_STATE_RUN,只能在这三种之间取值,0表示啥也没有等待调度,SCHED表示已经调度,RUN表示该tasklet正在运行。

已经被调度的tasklet结构体存放在两种单处理器数据结构当中,分别是tasklet_vec(普通优先级的tasklet)和tasklet_hi_vec(高优先级的tasklet),几乎没区别,只是优先级不一样,调度的步骤如下

运行的步骤如下:

其实tasklet给人的感觉就是一个对软中断的封装的简单接口而已。。

每个处理器都有一组辅助处理软中断(当然也就包括tasklet)的内核线程,那么什么时候执行这些软中断呢,上面在软中断部分也阐述了,但是这样有个问题,那就是软中断如果继续调软中断,就会不停的执行软中断。。这样在处理器负载很严重的时候就不太好了,会导致用户空间进程饥饿,还有一种方案,那就是并不立即处理软中断,而是等待一段时间,但是在处理器比较闲的时候这么做很显然不太好,因为完全可以立即执行你却让处理器闲着。作为改进,当大量软中断出现的时候,内核会唤醒一组内核线程来处理这些负载,关键来了,这些带着软中断的线程的优先级会被设置到最低的优先级上(nice值取最高为19),这样的会在处理器比较忙的时候,这些软中断不会跟用户空间进程争夺处理器资源,而且最终一定会被执行,处理器空闲的时候也可以直接得到运行。

  • 工作队列

工作队列是另外一种比较新的将工作推后的形式,和之前的两种处理方式不同,它会把工作交给一个内核线程去执行,这就意!味!着!是由进程上下文来处理了!就可以睡眠了!!(中断是不允许睡眠的)所以很简单就可以在这两种方法之间做出选择。

每一个处理器都有一个对应的工作者线程

表示工作的数据结构

这些工作的结构体被连城链表,当链表上的所有工作都做完了之后,线程就会休眠

实现方式也很简单,

  1. 线程首先把自己设置为休眠状态(只是设置,并没有立即进入休眠)并把自己加入等待队列
  2. 如果工作链表是空的,就用schedule()调度函数进入睡眠状态
  3. 如果链表中有对象,线程就不会睡眠了,就把自己的状态改为TASK_RUNNING,然后从等待队列中出来
  4. 如果链表非空,执行那些被退后的下半部分应该干的工作(就是循环一直找。。。)

来个结构图

  • 下半部机制的选择

这三种看上去都不错,那么应该怎么选择呢

如果你对共享有很高的要求,虽然比较麻烦,但还是使用软中断吧,因为可以各种操作(虽然保障这些很麻烦)

如果你不是对共享有那么高的要求,推荐使用tasklet,因为两种同类型的tasklet不能同时并行

如果你想在进程上下文中解决下半部分的问题,使用工作队列吧,当然如果你想睡眠,你也没得选了

* 全剧终*

时间: 2024-11-02 14:59:24

Linux内核实现中断和中断处理(二)的相关文章

再思linux内核在中断路径内不能睡眠/调度的原因(2010)【转】

转自:http://blog.csdn.net/maray/article/details/5770889 Linux内核中断路径中不能睡眠,为什么? 这里就行了很深入的讨论,值得一看:http://bbs2.chinaunix.net/viewthread.php?tid=1618430 但是,他们的讨论最后没有得出一个明确的结论.其中,cskyrain在8楼 的思考触及到了一个要点,但是没有深入展开: [c-sharp] view plain copy 1楼 发表于 2009-11-24 2

深入理解Linux内核-中断和异常

Linux内核代码查看 http://androidxref.com/ 中断:被定义位一个事件,它能改变处理器执行指令的顺序.它对应硬件(CPU.其他硬件设备)电路产生的电信号. 同步中断:指令执行时CPU控制单元产生:称为同步,是因为只有在一条指令终止执行后CPU才回发出中断.也被称为异常 异步中断:其他硬件设备按照CPU时钟信号随机产生的.也被简称中断 中断的约束:1.中断必须尽快处理完成:中断一般被分两部分执行:关键而且紧急的部分,内核立即执行:其余部分内核稍后执行: 2.中断的处理必须能

Linux内核启动流程分析(二)【转】

转自:http://blog.chinaunix.net/uid-25909619-id-3380544.html S3C2410 Linux 2.6.35.7启动分析(第二阶段) 接着上面的分析,第一阶段的代码跳转后,会进入第二阶段的代码. 第二阶段的代码是从\arch\arm\kernel\head.S开始的. 内核启动第二阶段主要完成的工作有,cpu ID检查,machine ID(也就是开发板ID)检查,创建初始化页表,设置C代码运行环境,跳转到内核第一个真正的C函数startkerne

[linux内核][LINUX内核编程]学习笔记(二)

linux内核————队列 linux内核——队列 定义: [cpp] view plaincopy struct __kfifo{ unsigned int in;  //入队偏移,写索引 unsigned int out;  //出队偏移,读索引 unsigned int mask; unsigned int esize; void *data; } 使用: 创建一个队列,该函数创建并初始化一个大小为size的kfifo: [cpp] view plaincopy 38 int __kfif

[数据结构][LINUX内核编程]学习笔记(二)

linux内核————队列 linux内核——队列 定义: [cpp] view plaincopy struct __kfifo{ unsigned int in;  //入队偏移,写索引 unsigned int out;  //出队偏移,读索引 unsigned int mask; unsigned int esize; void *data; } 使用: 创建一个队列,该函数创建并初始化一个大小为size的kfifo: [cpp] view plaincopy 38 int __kfif

Linux内核ROP姿势详解(二)

/* 很棒的文章,在freebuf上发现了这篇文章上部分的翻译,但作者貌似弃坑了,顺手把下半部分也翻译了,原文见文尾链接 --by JDchen */ 介绍 在文章第一部分,我们演示了如何找到有用的ROP gadget并为我们的系统(3.13.0-32 kernel –Ubuntu 12.04.5 LTS)建立了一个提权ROP链的模型.我们同时也开发了一个有漏洞的内核驱动来允许实现执行任意代码.在这一部分,我们将会使用这个内核模块来开发一个具有实践意义的ROP链:提权,修复系统,纯净退出到用户空

Linux内核同步机制之(二):Per-CPU变量

转自:http://www.wowotech.net/linux_kenrel/per-cpu.html 一.源由:为何引入Per-CPU变量? 1.lock bus带来的性能问题 在ARM平台上,ARMv6之前,SWP和SWPB指令被用来支持对shared memory的访问: SWP <Rt>, <Rt2>, [<Rn>] Rn中保存了SWP指令要操作的内存地址,通过该指令可以将Rn指定的内存数据加载到Rt寄存器,同时将Rt2寄存器中的数值保存到Rn指定的内存中去.

linux内核是中断下半部

首先阐述下为什么内核要将中断分成上下半部 因为中断本身打断了正常的程序执行,中断中不能进行任务调度,所以中断需要快返回,但是某些操作必须在中断中执行. 如果内核需要执行一个硬件相关.时间敏感.不能被中断的操作,那么这些操作就应该放到上半部中,其他能够推迟的操作应该放到下半部中去,这样完成了中断中必须完成的操作,又能很好的进行调度. 看看内核对于下半部的支持 首先说以下如何添加自己的软中断程序 首先添加自己的软中断类型,值越低优先级越高 用open_softirq增加相对应的中断处理函数 用rai

linux 内核的futex系统调用 (二)

futex 设计成用户空间快速锁操作,由用户空间实现fastpath,以及内核提供锁竞争排队仲裁服务,由用户空间使用futex系统调用来实现slowpath.futex系统调用提供了三种配对的调用接口,满足不同使用场合的,分别为noraml futex,pi-futex,以及 requeue-pi. futex的同步(锁)状态定义由用户空间去执行,futex系统调用并不需要理解用户空间是如何定义和使用这个地址对齐的4字节长的整型的futex,但是pi-futex除外,用户空间必须使用futex系