一、问题
1、内核调度与中断/异常/系统调用的关系如何?
2、信号处理与中断/异常/系统调用的关系如何?
3、内核抢占与中断/异常/系统调用的关系如何?

4、内核线程的调度有何特别之处?中断/异常/系统调用返回时,内核线程会发生调度吗?

这些问题都需要分析清楚中断/异常的返回流程,才能解答。

二、中断/异常的返回流程
1、中断/异常的返回内核软件流程
中断、异常(包括系统调用)和fork返回的处理流程如下:
 

关键点:
1)中断返回和异常返回的流程基本一致,差别主要在于异常返回时,需要先关一次中断。因为Linux实现中,异常使用的是陷阱门,通过时不会自动关中断;而中断使用的是中断门,通过时会自动关中断。
2)中断/异常(包括系统调用)返回时,是进行调度(schedule)的重要时机点,其中,中断(时钟中断)返回时调度依赖的最主要的时机点,时钟中断处理函数中不会直接进行调度,只是根据相应的调度算法,决定是否需要调度,以及调度的next task,如果需要调度,则设置NEED_RESCHED标记。调度(schedule)的实际执行是在中断返回的时候,检查NEED_RESCHED标记,如果设置则进行调度。
3)信号处理是在当前进程从内核态返回用户态时进行的,在发生中断、异常(包括系统调用)、或fork时,都有可能从内核态返回用户态,都是处理信号的时机。注意:只有current进程的信号才能在此时得到处理。其它非正在运行的进程的信号无法处理。
4)关于内核抢占。中断/异常发生在内核态时,也就是说中断/异常返回时,需要返回内核态,走resume_kernel流程,此时,如果内核支持内核抢占,则此时是个关键的调度时机点,如果内核不支持抢占,则不会发生调度。也就是说:如果当前进程上下文处于内核态,当不支持内核抢占时,则无论进程的优先级和时间片如何,都是不能发生调度的,只能在返回用户态时,才能发生调度。从这点可以看出,当不支持内核抢占时,Linux的实时性很差(开启内核抢占后稍好),当在内核态(中断、软中断、其它内核流程)执行时间或流程太长时,可能导致进程调度饥饿,极端情况下,当在内核态发生死锁时,会直接导致整个系统因无法调度而死锁,当然针对这种情况(softlockup),内核提供了专门的watchdog机制来检测。

5)关于内核线程的调度,跟普通线程相比,从原理和机制上看,没有特别之处。但关键的不同在于:内核线程始终运行在内核态,当没有开启内核抢占时,设想当一个内核线程被中断/异常打断,此时从中断/异常返回时会发生调度吗?答案是不会,因为当前进程的上下文处于内核态,在没有开启内核抢占的情况下,是不会发生调度行为的,除非该内核线程主动调用schedule()释放CPU控制权。也就是说,内核线程触发主动调用schedule,否则会一直占用CPU。所以在编写内核线程时,需要在相关任务处理结束后,主动调用schedule,这点需要注意。
 
2、中断/异常返回时硬件完成的处理流程
中断或异常返回时,必然会执行iret指令,然后将控制器交回给之前被中断打断的进程,硬件自动完成如下操作:
1)从当前栈(内核栈)中弹出cs、eip和eflag,并load到相应的寄存器中寄存器。(如之前有硬件错误码入栈,需要先弹出这个错误码)。 
2)权限检查。比对ISR的CPL是否等于cs中的低两位的值。如果是,iret终止返回;否则,转入下一步。 
3)从当前栈(内核栈)中弹出之前压入的用户态堆栈相关的ss和esp,并load到相应寄存器,至此,即完成了从内核栈到用户栈的切换。 
4)后续处理。主要包括:检查ds、es、fs及gs段寄存器,如果其中一个寄存器包含的选择符是一个段描述符,并且其DPL值小于CPL,那么,清相关的段寄存器。目的是为了防止用户态的程序利用内核以前所用的段寄存器,以防止恶意用户程序利用其访问内核地址空间。

三、代码分析
中断、异常(包括系统调用)、fork返回时,会分别跳转到entry_32.S汇编代码中的ret_from_intr、ret_from_exception、ret_from_fork标号处执行。相应代码分析如下:
1、中断返回(ret_from_intr)
1)主流程

点击(此处)折叠或打开

  1. /*从中断返回*/
  2. ret_from_intr:
  3. /*将当前进程的thread_info结构体的指针存入%ebp帧寄存器中*/
  4. GET_THREAD_INFO(%ebp)
  5. #ifdef CONFIG_VM86
  6. /* 取中断之前寄存器EFLAGS的高16位和段寄存器CS的内容构成的32位长整数放入eax中,其目的是检验:
  7. * 1.中断之前CPU是否运行于VM86模式
  8. * (EFLAGS的高16位中的第二位用来标识CPU运行在VM86模式下)
  9. * 2.中断之前CPU运行于用户空间还是系统空间
  10. *(CS的低两位代表着中断发生时CPU的运行级别CPL。若是CS的低两位为1,表示中断发生于用户空间,)
  11. */
  12. movl PT_EFLAGS(%esp), %eax    # mix EFLAGS and CS
  13. movb PT_CS(%esp), %al
  14. andl $(X86_EFLAGS_VM | SEGMENT_RPL_MASK), %eax
  15. #else
  16. /*
  17. * We can be coming here from child spawned by kernel_thread().
  18. */
  19. movl PT_CS(%esp), %eax
  20. andl $SEGMENT_RPL_MASK, %eax
  21. #endif
  22. // 判断是否返回用户态或者v8086模式,如果不是,则转入resume_kernel,否则进入resume_userspace
  23. cmpl $USER_RPL, %eax
  24. jb resume_kernel    # not returning to v8086 or userspace

2)返回用户态

点击(此处)折叠或打开

  1. // 如果是返回用户态
  2. ENTRY(resume_userspace)
  3. LOCKDEP_SYS_EXIT
  4. /*
  5. * 前面已经关了中断了,这次再关的原因是,还有其它流程会自己跳转
  6. * 到这里,比如system_call
  7. */
  8. DISABLE_INTERRUPTS(CLBR_ANY)    # make sure we don

3)调度和信号处理:

点击(此处)折叠或打开

  1. work_pending:
  2. # 返回用户态时,只需要判断need_resched是否置位,不需要判断preempt_count
  3. # 如果need_resched置位,则发生调度,否则跳转到work_notifysig
  4. testb $_TIF_NEED_RESCHED, %cl
  5. # 进行信号处理
  6. jz work_notifysig
  7. work_resched:
  8. # 需要调度,调用schedule函数
  9. call schedule
  10. # 调度返回,注意:到这里已经是新的进程上下文了,后面有机会处理信号
  11. LOCKDEP_SYS_EXIT
  12. # 关中断
  13. DISABLE_INTERRUPTS(CLBR_ANY)    # make sure we don't miss an interrupt
  14. # setting need_resched or sigpending
  15. # between sampling and the iret
  16. # 关闭trace irq功能
  17. TRACE_IRQS_OFF
  18. movl TI_flags(%ebp), %ecx
  19. # 再次确认是否还有其它事情处理
  20. andl $_TIF_WORK_MASK, %ecx    # is there any work to be done other
  21. # than syscall tracing?
  22. # 如果没有,则恢复上下文
  23. jz restore_all
  24. # 如果有,再次检查是否需要调度,如果需要,则再次跳转到work_resched进行重新调度
  25. testb $_TIF_NEED_RESCHED, %cl
  26. jnz work_resched
  27. # 如果不需要调度,则继续到work_notifysig,进行信号处理了,也就是说如果这里发生调度,也是有机会处理信号的。
  28. work_notifysig:                # deal with pending signals and
  29. # notify-resume requests
  30. #ifdef CONFIG_VM86
  31. testl $X86_EFLAGS_VM, PT_EFLAGS(%esp)
  32. movl %esp, %eax
  33. jne work_notifysig_v86        # returning to kernel-space or
  34. # vm86-space
  35. 1:
  36. #else
  37. movl %esp, %eax
  38. #endif
  39. # 开trace
  40. TRACE_IRQS_ON
  41. # 开中断(前面关了),意味着信号处理是开中断执行的,还是中断优先级高
  42. ENABLE_INTERRUPTS(CLBR_NONE)
  43. # 再次判断CS低两位,当为1时,表示中断/异常之前处于用户态,否则为内核态,据此再次确认是否需要返回内核态
  44. movb PT_CS(%esp), %bl
  45. andb $SEGMENT_RPL_MASK, %bl
  46. cmpb $USER_RPL, %bl
  47. # 返回内核态
  48. jb resume_kernel
  49. # edx清零
  50. xorl %edx, %edx
  51. # 调用C函数,其中进行通知链即信号的处理
  52. call do_notify_resume
  53. # 信号处理完后,重新跳转到resume_userspace,此时如果没有新的信号产生,则会在前面就通过restore_all恢复了,不会再到这里了
  54. jmp resume_userspace
  55. #ifdef CONFIG_VM86
  56. ALIGN
  57. work_notifysig_v86:
  58. pushl_cfi %ecx            # save ti_flags for do_notify_resume
  59. call save_v86_state        # %eax contains pt_regs pointer
  60. popl_cfi %ecx
  61. movl %eax, %esp
  62. jmp 1b
  63. #endif
  64. END(work_pending)

4)返回内核态

点击(此处)折叠或打开

  1. /*如果配置了内核抢占*/
  2. #ifdef CONFIG_PREEMPT
  3. ENTRY(resume_kernel)
  4. /*
  5. * 前面已经关了中断了,这次再关的原因是,还有其它流程会自己跳转
  6. * 到这里,比如system_call
  7. */
  8. DISABLE_INTERRUPTS(CLBR_ANY)
  9. /*判断是否可以抢占*/
  10. cmpl $0,TI_preempt_count(%ebp)    # non-zero preempt_count ?
  11. /*抢占计数非0,不能抢占,则不产生调度,直接恢复上下文*/
  12. jnz restore_all
  13. /*可以抢占,则需要调度*/
  14. need_resched:
  15. /*判断need_resched是否被设置*/
  16. movl TI_flags(%ebp), %ecx    # need_resched set ?
  17. testb $_TIF_NEED_RESCHED, %cl
  18. /*没设置need_resched,则不需要调度,直接恢复上下文*/
  19. jz restore_all
  20. /*
  21. *判断发生中断时(因为PT_EFLAGS(%esp)中保存的是进入中断时的EFLAGS值,这是由CPU硬件自动压栈的,中断走中断门,会自动关中断,异常走陷阱门,不自动关中断)是否关中断了,
  22. *如果关了,表示是异常上下文(Fixme:应该是中断吧),则直接恢复上下文。
  23. */
  24. testl $X86_EFLAGS_IF,PT_EFLAGS(%esp)    # interrupts off (exception path) ?
  25. jz restore_all
  26. /*如果没关中断,表示为中断上下文?则调用preempt_schedule_irq,进行调度*/
  27. call preempt_schedule_irq
  28. jmp need_resched
  29. END(resume_kernel)

2、异常返回(ret_from_exception)
异常返回跟中断返回流程基本一致,差别主要在于异常返回时,需要先关一次中断。因为Linux实现中,异常使用的是陷阱门,通过时不会自动关中断;而中断使用的是中断门,通过时会自动关中断。

点击(此处)折叠或打开

  1. /*从异常返回*/
  2. ret_from_exception:
  3. /*
  4. * 这里为什么要关中断?而从中断返回不需要? 因为异常走的是陷阱门,
  5. * 默认是不关中断执行的,而中断走的是中断门,默认是关中断执行的?
  6. *
  7. */
  8. /*关中断*/
  9. preempt_stop(CLBR_ANY)
  10. /*从中断返回*/
  11. ret_from_intr:
  12. ...

3、fork返回(ret_from_fork)
fork返回的后半部分处理跟异常/中断返回一致,前面一部分有单独的处理:包括调用schedule_tail和跳转syscall_exit进行相关处理

点击(此处)折叠或打开

  1. #fork返回,单独处理
  2. ENTRY(ret_from_fork)
  3. CFI_STARTPROC
  4. pushl_cfi %eax
  5. #进行调度收尾处理,包括回收DEAD(X)状态的进程
  6. call schedule_tail
  7. #获取thread_info放入ebp中
  8. GET_THREAD_INFO(%ebp)
  9. popl_cfi %eax
  10. #重设kernel eflags
  11. pushl_cfi $0x0202    # Reset kernel eflags
  12. popfl_cfi
  13. #跳转到syscall_exit进行系统调用退出相关的处理。
  14. jmp syscall_exit
  15. CFI_ENDPROC
  16. END(ret_from_fork)

原文地址: http://blog.chinaunix.net/uid-14528823-id-4761421.html

kernel 3.10内核源码分析--中断--中断和异常返回流程相关推荐

  1. Linux kernel 3.10内核源码分析--进程上下文切换

    一.疑问 进程调度时,当被选中的next进程不是current进程时,需要进行上下文切换. 进行上下文切换时,有一些问题不太容易理解,比如: 1.进程上下文切换必然发生在内核态吗? 2.上下文切换后原 ...

  2. Linux Kernel 3.10内核源码分析--块设备层request plug/unplug机制

    一.基本原理 Linux块设备层使用了plug/unplug(蓄流/泄流)的机制来提升IO吞吐量.基本原理为:当IO请求提交时,不知直接提交给底层驱动,而是先将其放入一个队列中(相当于水池),待一定时 ...

  3. Linux kernel 3.10内核源码分析--TLB相关--TLB概念、flush、TLB lazy模式

    一.概念及基本原理 TLB即Translation Lookaside Buffer,是MMU中的一种硬件cache,用于缓存页表,即缓存线性地址(虚拟地址)到物理地址的映射关系. 如果没有TLB,那 ...

  4. kernel 3.10内核源码分析--内核栈及堆栈切换

    1.概念 Linux中有3种栈: 1)用户栈.当进程处于用户态时使用,位于进程地址空间(用户态部分(如:0-0xc0000000))底部,用户态分配局部变量和函数调用时时,使用该栈,跟平时我们见到和理 ...

  5. Linux kernel 3.10内核源码分析--slab原理及相关代码

    1.基本原理 我们知道,Linux保护模式下,采用分页机制,内核中物理内存使用buddy system(伙伴系统)进行管理,管理的内存单元大小为一页,也就是说使用buddy system分配内存最少需 ...

  6. Linux kernel 3.10内核源码分析--进程退出exit_code

    进程退出时,有相应的exit_code,可用于判断进程退出的原因. 比如,waitpid()接口用于等待进程退出,此时被等待退出的进程的返回值比较重要,需要用其来判断进程退出的相应状态,而这就是通过进 ...

  7. ernel 3.10内核源码分析--KVM相关--虚拟机运行

    1.基本原理 KVM虚拟机通过字符设备/dev/kvm的ioctl接口创建和运行,相关原理见之前的文章说明. 虚拟机的运行通过/dev/kvm设备ioctl VCPU接口的KVM_RUN指令实现, 在 ...

  8. v42.05 鸿蒙内核源码分析(中断切换) | 系统因中断活力四射 | 百篇博客分析鸿蒙源码

    子曰:"知者不惑,仁者不忧,勇者不惧." <论语>:子罕篇 百篇博客系列篇.本篇为: v42.xx 鸿蒙内核源码分析(中断切换篇) | 系统因中断活力四射 硬件架构相关 ...

  9. 鸿蒙内核分析,鸿蒙内核源码分析(中断概念篇) | 外人眼中权势滔天的当红海公公...

    关于中断部分系列篇将用三篇详细说明整个过程. ● 中断概念篇 中断概念很多,比如中断控制器,中断源,中断向量,中断共享,中断处理程序等等.本篇做一次整理.先了解透概念才好理解中断过程.本篇的主角是海公 ...

最新文章

  1. TorchScript神经网络集成技术
  2. 美国防部官员讨论量子科学、5G和定向能的发展
  3. Windows Server中的故障转移群集的实现机制
  4. SAPscripts 到导数程序中取数据的实例
  5. js 浅拷贝直接赋值_js 深拷贝 vs 浅拷贝
  6. 4-Qt6控制台项目信号与槽
  7. iOS面试题02-UI篇
  8. Django项目配置mysql主从数据库实现读写分离
  9. python二分法查找时间点_python有序查找算法:二分法
  10. VS2010删除所有断点时不弹出提示窗口
  11. 将区块链哈希转化为文字标题?IPSE哈希技术Hashlink解释
  12. Linux文件系统选择
  13. Snmp网络协议及Java开发相关
  14. GitHub遭遇史上最强DDoS攻击:峰值流量1.35Tbps!
  15. mysql详细教程大全
  16. MATLAB 矩阵及运算
  17. java读取txt文件入库_java读取txt文件批量入库
  18. 正三角形二面体群表示为二阶矩阵形式
  19. 测试一枚-记录工作-覆盖率(二)
  20. FOFA网络空间搜索引擎使用教程

热门文章

  1. c++工程师面试常见问题之c++中四种cast转换
  2. Python学习笔记:Dict和Set
  3. 程序练习:Matlab 实现最小二乘法
  4. Linux内存申请机制
  5. OpenMP基本概念
  6. C#字节数组与值类型变量的互相转换,以及注意事项
  7. SCons命令 之 从入门到精通
  8. Softmax vs. SoftmaxWithLoss 推导过程
  9. [实践篇] Softmax Regression
  10. SESSION常见问题辑