首页 > 技术文章 > 中断

liushuhe1990 2018-09-10 15:31 原文

1.进程上下文:

(1)进程上文:其是指进程由用户态切换到内核态是需要保存用户态时cpu寄存器中的值,进程状态以及堆栈上的内容,即保存当前进程的进程上下文,以便再次执行该进程时,能够恢复切换时的状态,继续执行。
(2)进程下文:其是指切换到内核态后执行的程序,即进程运行在内核空间的部分。

2.中断上下文:

(1)中断上文:硬件通过中断触发信号,导致内核调用中断处理程序,进入内核空间。这个过程中,硬件的一些变量和参数也要传递给内核,内核通过这些参数进行中断处理。中断上文可以看作就是硬件传递过来的这些参数和内核需要保存的一些其他环境(主要是当前被中断的进程环境。
(2)中断下文:执行在内核空间的中断服务程序。

3、中断上半部、下半部

1、Linux 的中断处理分为两个半部,顶半部处理紧急的硬件操作,底半部处理不紧急的耗时操作。
2、tasklet 和工作队列都是调度中断底半部的良好机制,tasklet 基于软中 断实现。内核定时器也依靠软中断实现

三.为什么要进行不同之间状态的切换

在现在操作系统中,内核功能模块运行在内核空间,而应用程序运行在用户空间。现代的CPU都具有不同的操作模式,代表不同的级别,不同的级别具有不同的功能,其所拥有的资源也不同;在较低的级别中将禁止使用某些处理器的资源。Linux系统设计时利用了这种硬件特性,使用了两个级别,最高级别和最低级别,内核运行在最高级别(内核态),这个级别几乎可以使用处理器的所有资源,而应用程序运行在较低级别(用户态),在这个级别的用户不能对硬件进行直接访问以及对内存的非授权访问。内核态和用户态有自己的内存映射,即自己的地址空间。当工作在用户态的进程想访问某些内核才能访问的资源时,必须通过系统调用或者中断切换到内核态,由内核代替其执行。进程上下文和中断上下文就是完成这两种状态切换所进行的操作总称。我将其理解为保存用户空间状态是上文,切换后在内核态执行的程序是下文。

四.什么情况下进行用户态到内核态的切换

1.进程上下文主要是异常处理程序和内核线程。内核之所以进入进程上下文是因为进程自身的一些工作需要在内核中做。例如,系统调用是为当前进程服务的,异常通常是处理进程导致的错误状态等。
2.中断上下文是由于硬件发生中断时会触发中断信号请求,请求系统处理中断,执行中断服务子程序。

五.中断上下文代码中注意事项

运行于进程上下文的内核代码是可抢占的,但中断上下文则会一直运行至结束,不会被抢占。所以中断处理程序代码要受到一些限制,在中断代码中不能出现实现下面功能的代码:

(1)睡眠或者放弃CPU。 
   因为内核在进入中断之前会关闭进程调度,一旦睡眠或者放弃CPU,这时内核无法调度别的进程来执行,系统就会死掉。牢记:中断服务子程序一定不能睡眠(或者阻塞)。
(2)尝试获得信号量 
   如果获得不到信号量,代码就会睡眠,导致(1)中的结果。
(3)执行耗时的任务 
   中断处理应该尽可能快,因为如果一个处理程序是IRQF_DISABLED类型,他执行的时候会禁止所有本地中断线,而内核要响应大量服务和请求,中断上下文占用CPU时间太长会严重影响系统功能。中断处理程序的任务尽可能放在中断下半部执行。
(4)访问用户空间的虚拟地址 
   因为中断运行在内核空间。 

根据中断入口跳转方法的不同,中断分为向量中断和非向量中断 ,向量中断由硬件提供中断服务程序入口地址,非向量中断由软件提供中断服务程序入口地址 
下列 3 个函数用于屏蔽一个中断源

void disable_irq(int irq);
void disable_irq_nosync(int irq);
void enable_irq(int irq);

 disable_irq_nosync()disable_irq()的区别在于前者立即返回,而后者等待目前的中断处理完成 

在中断顶半部调用disable_rq()会引起系统死锁。因此在顶半部只能调用disable_irq_nosync()

一、软中断

     软中断也是一种传统的底半部的处理机制,软中断运行于中断上下文,仍然属于原子上下文的一种,而工作队列运行于进程上下文

    因此,软中断和tasklet处理函数中不允许睡眠,工作队列允许睡眠

总结一下硬中断、软中断和信号的区别:硬中断是外部设备对 CPU 的中断,软中断通常是硬中断服务程序对内核的中断,而信号则是由内核(或其他进程)对某个进程的中断 
二、中断定时器

  1、timer_list

  Linux 内核中,timer_list 结构体的一个实例对应一个定时器

 struct timer_list {
    struct list_head entry; //定时器列表
    unsigned long expires; //定时器到期时间
    void (*function)(unsigned long); //定时器处理函数
    unsigned long data; //作为参数被传入定时器处理函数
    struct timer_base_s *base;
 };

  2、初始化定时器

void init_timer(struct timer_list * timer);

  3、增加定时器

void add_timer(struct timer_list * timer);

  4、删除定时器 

int del_timer(struct timer_list * timer);

  del_timer_sync()del_timer()的同步版,在删除一个定时器时需要等待其被处理完,因此该函数的调用不能发生在中断上下文中,主要在多处理器系统中使用,如果编译内核时不支持 SMPdel_timer_sync()和 del_timer()等价。 




 



 

推荐阅读