burningcodes  原创作品转载请注明出处《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

上一篇文章中分析了系统调用的基本原理,通过跟踪源码将应用层系统调用和内核层的系统调用联系在了一起,回顾点这: 理解系统调用的原理(一)

这篇文章在上文的基础上主要解决两个问题:

  • 进一步分析system_call中做了哪些操作
  • 中断处理与系统调用的区别和联系

1.再看system_call

上篇文章已经介绍过system_call的主要流程,但事实上CPU在处理完call *sys_call_table后还做了一些十分重要的操作,比如处理当前进程未处理的信号,进行被动调度等。在执行完真正的系统调用后先是保存返回值:

注意到两句关键代码,在函数调用返回前先关中断:

关中断之后进入syscall_exit_work(linux-3.18.6/arch/x86/kernel/entry_32.S):

在work_pending中先会判断是否置了NEED_RESCHED位,如果置位了就执行work_resched段代码,被动调度当前进程,若没置位的话就继续处理当前进程未处理的信号:

无需调度的话就处理信号:

从上面的代码中可以看出,在系统调用或者中断,异常返回到用户空间之前内核都会检查是否有信号在当前进程中挂起。如果有信号就调用函数do_notify_resume处理信号。

用一张流程图来总结sys_after_call到iret之间的主要流程(省略了一些细节):

liuchent

总之,在系统调用返回用户态之前会继续完成两件事:

  • 被动调度当前进程
  • 处理当前进程还未处理的信号

2.中断处理和系统调用的区别和联系

中断可以分成三类:

  • CPU外部中断。比如I/O中断,时钟中断。这类中断是被动产生的。
  • CPU内部中断。又可称为异常,比如地址越界,非法操作数等。这类中断也是被动产生的。
  • 系统调用。又叫trap(陷阱),这类中断是主动产生的。

在执行start_kernel时内核调用trap_init()函数初始化了中断门(回顾看这从源码中跟踪Linux Kernel的启动过程):

无论是set_intr_gate还是set_system_intr_gat最终都会调用_set_gata(linux-3.18.6/arch/x86/include/asm/desc.h

通过write_idt_entry将某中断的信息写入IDT(Interrupt Descriptor Table,中断描述符表),而我们所熟悉的0x80号中断也保存在这张表中,每个表项保存了这个中断的偏移,权限等内容。所有三类中断都在IDT表中占有唯一的一项,在调用中断处理函数时需要通过IDT和GDT(Global Descriptor Table,全局描述符表)配合找到处理函数的真实地址,也就是说无论是哪种中断,他们在调用流程上都类似,只不过最后调用了不同的处理函数:

zhongduan

3.总结

通过分析我们知道了在调用system_call时,内核除了要完成指定的系统调用外还需要处理进程收到但还未处理的信号,并且有可能执行被动调度。另外还简单分析了系统调用和中断处理的区别和联系,简单而言,系统调用只是中断的一个子集,但是不同类型的中断(异常,I/O中断等)大致的调用流程都是类似的。

以上都是个人理解,如有哪里不对欢迎指正交流。

4.参考文献

http://blog.chinaunix.net/uid-27767798-id-3507139.html

http://www.ibm.com/developerworks/cn/linux/l-cn-linuxkernelint/

http://blog.csdn.net/yanlinwang/article/details/8169725

http://blog.csdn.net/fwqcuc/article/details/5855460

 

 

观看更多有关 的文章?

*

+
跳转到评论