一.Linux系统,陷入内核的三种方式
1.系统调用:应用程序主动像操作系统发出的服务请求,系统调用是应用程序主动让内核代替进程执行在内核空间,可能同步也可能异步。
2.中断:一般由外设(硬件)发送电信号到处理器引起的,处理器接收中断后,会马上向操作系统反应信号的到来,由操作系统处理新来的数据,硬件产生中断的过程不考虑时钟同步(随时产生),也称为异步中断。
3.异常:在产生时必须考虑与处理器时钟同步,又称为同步中断,在处理器执行到由于程序失误而导致的错误指令(如被0除)的时候,或者是在执行期间出现特殊情况(缺页),处理器就会产生一个异常。
二.中断处理机制的实现
1.中断从硬件到内核的路由
从本质上来讲,中断是一种电信号,当设备有某种事件发生时,它就会产生中断,通过总线把电信号发送给中断控制器。如果中断的线是激活的,中断控制器就把电信号发送给处理器的某个特定引脚。处理器于是立即停止自己正在做的事,跳到中断处理程序的入口点,进行中断处理。(上述描述的是硬中断,软中断利用指令信号触发,处理过程基本一致。)
2.查看Linux下中断系统
1)文件/proc/interrupts包含系统中断号,在cpu上发送中断的次数,设备接口、中断名称等信息
1)第一列表示IRQ号, 第二、三列表示相应的CPU核心被中断的次数, 第四列表示设备接口,第五列表示中断名称,如timer(系统时钟)被CPU0中断44次
2)IRQ号决定了需要被CPU处理的优先级,IRQ号越小意味着优先级越高,如 IRQ0 :系统时钟(不能改变),IRQ1 :键盘控制器(不能改变)
2)文件/proc/stat
以intr开头的信息记录了机器从启动开始,依赖各个中断号发送中断次数,第一个数字3813161表示总的中断数目,之后数字分别表示从中断号0开始各个中断号中断的次数。
2)文件/proc/irq
/proc/irq目录下面会为每个注册的irq创建一个以irq编号为名字的子目录,每个子目录下分别有以下条目:
smp_affinity :irq和cpu之间的亲缘绑定关系;
smp_affinity_hint :只读条目,用于用户空间做irq平衡只用;
spurious :可以获得该irq被处理和未被处理的次数的统计信息;
Linux 2.4内核之后引入了将特定中断绑定到指定的CPU的技术,称为SMP IRQ affinity。该技术允许你限制或者重新分配服务器的工作负载, 从而让服务器更有效的工作. 以网卡中断为例,在没有设置SMP IRQ affinity时,所有网卡中断都关联到CPU0, 这导致了CPU0负载过高,而无法有效快速的处理网络数据包,导致了瓶颈。 通过SMP IRQ affinity,把网卡多个中断分配到多个CPU上,可以分散CPU压力,提高数据处理速度。
smp_affinity文件中的数值以十六进制显示,如 /proc/irq/0/smp_affinity内容为f, /proc/irq/1/smp_affinity/内容为3,对应的二进制为1111,0011,二进制表示中每一位代表一个CPU,1表示该CPU可以中断该IRQ,即把0号中断绑定到前4个CPU(CPU0-3)上面,把1号中断绑定到前2个CPU(CPU0-2)上面,(当然我的主机只有2个CPU);
/proc/irq/default_smp_affinity 指定了默认情况下未激活的IRQ的中断亲和掩码(affinity mask)。一旦IRQ被激活,它将被设置为默认的设置(即default_smp_affinity中记录的设置),该文件能被修改,默认设置是f。
三.中断的分类
1.硬中断
1)硬中断是由硬件产生的,比如,像磁盘,网卡,键盘,时钟等。每个设备或设备集都有它自己的IRQ(中断请求)。基于IRQ,CPU可以将相应的请求分发到对应的硬件驱动上(注:硬件驱动通常是内核中的一个子程序,而不是一个独立的进程)。
2)处理中断的驱动是需要运行在CPU上的,因此,当中断产生的时候,CPU会中断当前正在运行的任务,来处理中断。在有多核心的系统上,一个中断通常只能中断一颗CPU。
2.软中断
1)由当前正在运行的进程所产生的
2)通常,软中断是一些对I/O的请求。这些请求会调用内核中可以调度I/O发生的程序。对于某些设备,I/O请求需要被立即处理,而磁盘I/O请求通常可以排队并且可以稍后处理。根据I/O模型的不同,进程或许会被挂起直到I/O完成,此时内核调度器就会选择另一个进程去运行。I/O可以在进程之间产生并且调度过程通常和磁盘I/O的方式是相同
3)软中断仅与内核相联系。而内核主要负责对需要运行的任何其他的进程进行调度。一些内核允许设备驱动的一些部分存在于用户空间,并且当需要的时候内核也会调度这个进程去运行。
4)软中断并不会直接中断CPU。也只有当前正在运行的代码(或进程)才会产生软中断。这种中断是一种需要内核为正在运行的进程去做一些事情(通常为I/O)的请求。有一个特殊的软中断是Yield调用,它的作用是请求内核调度器去查看是否有一些其他的进程可以运行
硬中断与软中断的区别:
①硬中断是由外部事件引起的因此具有随机性和突发性;软中断是执行中断指令产生的,无面外部施加中断请求信号,因此中断的发生不是随机的而是由程序安排好的。
②硬中断的中断响应周期,CPU需要发中断回合信号(NMI不需要),软中断的中断响应周期,CPU不需发中断回合信号。
③硬中断的中断号是由中断控制器提供的(NMI硬中断中断号系统指定为02H);软中断的中断号由指令直接给出,无需使用中断控制器。
④硬中断是可屏蔽的(NMI硬中断不可屏蔽),软中断不可屏蔽。
3.时钟中断
Linux的OS时钟的物理产生原因是可编程定时/计数器产生的输出脉冲,这个脉冲送入CPU,就可以引发一个中断请求信号,我们就把它叫做时钟中断。
时钟中断的主要工作是处理和时间有关的所有信息、决定是否执行调度程序以及处理下半部分。和时间有关的所有信息包括系统时间、进程的时间片、延时、使用CPU的时间、各种定时器,进程更新后的时间片为进程调度提供依据,然后在时钟中断返回时决定是否要执行调度程序。下半部分处理程序是Linux提供的一种机制,它使一部分工作推迟执行。
四.中断上下文与进程上下文
中断上下文是由于硬件发生中断时会触发中断信号请求,请求系统处理中断,执行中断服务子程序。
1.中断上下文:
(1)中断上文:硬件通过中断触发信号,导致内核调用中断处理程序,进入内核空间。这个过程中,硬件的一些变量和参数也要传递给内核,内核通过这些参数进行中断处理。中断上文可以看作就是硬件传递过来的这些参数和内核需要保存的一些其他环境(主要是当前被中断的进程环境。
(2)中断下文:执行在内核空间的中断服务程序。
2.进程上下文:
(1)进程上文:其是指进程由用户态切换到内核态是需要保存用户态时cpu寄存器中的值,进程状态以及堆栈上的内容,即保存当前进程的执行环境,以便再次执行该进程时,能够恢复切换时的状态,继续执行。
(2)进程下文:其是指切换到内核态后执行的程序,即进程运行在内核空间的部分。
3.中断上下文与进程上下文区别:
1)所谓的“进程上下文”,可以看作是用户进程传递给内核的这些参数以及内核要保存的那一整套的变量和寄存器值和当时的环境等;所谓的“ 中断上下文”,其实也可以看作就是硬件传递过来的这些参数和内核需要保存的一些其他环境(主要是当前被打断执行的进程环境)。
2)一个进程的上下文可以分为三个部分:用户级上下文、寄存器上下文以及系统级上下文。
(1)用户级上下文: 正文、数据、用户堆栈以及共享存储区;
(2)寄存器上下文: 通用寄存器、程序寄存器(IP)、处理器状态寄存器(EFLAGS)、栈指针(ESP);
(3)系统级上下文: 进程控制块task_struct、内存管理信息(mm_struct、vm_area_struct、pgd、pte)、内核栈等。
3)中断上下文和特定进程无关,通常都会始终占有CPU(当然中断可以嵌套,但我们一般不这样做),不可以被打断,所以运行在中断上下文的代码就要受一些限制:
1、睡眠或者放弃CPU。
这样做的后果是灾难性的,因为内核在进入中断之前会关闭进程调度,一旦睡眠或者放弃CPU,这时内核无法调度别的进程来执行,系统就会死掉
2、尝试获得信号量
如果获得不到信号量,代码就会睡眠,会产生和上面相同的情况
3、执行耗时的任务
中断处理应该尽可能快,因为内核要响应大量服务和请求,中断上下文占用CPU时间太长会严重影响系统功能。
4、访问用户空间的虚拟地址
因为中断上下文是和特定进程无关的,它是内核代表硬件运行在内核空间,所以在终端上下文无法访问用户空间的虚拟地址