【中断异常】系统调用

来源:互联网 发布:windows自带录制视频 编辑:程序博客网 时间:2024/04/30 05:11

外部中断是CPU被动的,异步的进入系统空间的一种手段,那么系统调用就是CPU主动的,同步的进入系统空间的手段;其实就是软件设计人员确切的知道执行哪一条指令以后就一定会进入系统空间;相比之下,中断有很大的不可预测性,但是它们都使CPU的运行状态从用户态转入系统态;当然,中断有可能发生在系统空间运行时,而系统调用只发生在用户空间;其实最大的原因在于CPU运行状态的变化,就是所谓的保护模式;在DOS系统中,不分用户态和系统态,系统调用只是动态链接库的库函数,通过中断指令int来实现,其实用户知道具体函数入口地址可绕过系统调用,直接调用这些函数;


(1)Linux的系统调用是通过中断指令“INT 0x80”完成的,所有的系统调用都要进入系统空间,在完成了所需要的服务后从系统空间返回的过程,比如sethostname()就是这样一个系统调用,设置主机名;将%eax存入0x4a(通过寄存器转入系统调用号),然后调用“INT 0x80”;如果用堆栈来传系统调用号,因为用户空间到系统空间要涉及到堆栈的切换,用户堆栈变为系统堆栈,虽然可以从用户空间读,但麻烦;而且从系统调用返回时,出错可设置好出错代码以及返回值;

(2)CPU穿过陷阱门和发生中断穿过中断门的过程是相同的,外部中断穿过中断门,是不需要检查中断门规定的级别,而INT指令穿过中断门或陷阱门时,要核对所规定的准入级别和CPU当前运行的级别;为系统调用设置的陷阱门的准入级别DPL为3;寄存器IDTR指向当前的中断向量表IDT,而IDT表中对应表项0x80的表项就是为INT 0x80设置的陷阱门,其中的函数指针是system_call();

(3)在system_call中,用orig_ax存放系统调用号(外部中断时是中断请求号),SAVE_ALL虽然一样,保存了多个寄存器,但程序自身的要求,只能使用最后压入的5个寄存器;GET_CURRENT使%ebx指向当前进程;查看系统调用号是否超出了范围,task_struct使用flags来设置PT_TRACESYS,从而跟踪子进程的系统调用;当PT_TRACESYS为1时,就要转入tracesys中;然后使用call来调用系调用跳转表sys_call_table[](是一个函数指针的集合,跳转时以系统调用号为下标在数组中找到对应的函数指针,目前linux2.4内核提供221个系统调用,其余的30余项可由用户自行定义,凡是没有定义下标的系统调用号,函数指针都指向sys_ni_syscall);凡是内核不支持的系统调用号全部指向sys_ni_syscall中,这个函数只返回一个出错代码-ENOSYS,表示该系统调用尚未实现;0x4a是sys_sethostname();

(4)在sys_sethostname()中,首先使用capable()来检查当前进程是否享有CAP_SYS_ADMIN权限;修改主机名时,要防止多个CPU来修改,故要写保护;然后就是copy_from_user()从用户空间和系统空间复制数据;

(5)在copy_from_user()中,除非2^n大小字节的数据,一般通过__generic_copy_from_user(),在__generic_copy_from_user()中,先使用access_ok来检查from和n的合理性,看(from+n)是否超过了用户空间的上限,而并不检查该区间是否映射;然后通过另一个宏操作__copy_user_zeroing()从用户空间复制;

(6)在__copy_user_zeroing()中,是用汇编写的,拷贝用户空间的数据;不检查用户指针的合法性,应为大多数的传入的用户空间指针都是合法的,为了效率,没有检查;一旦出错,do_page_fault()中真的碰到了坏指针,就需要来修复,否则一旦再访问,会一直出错,具体的修复,就是将CPU在异常返回时将要重新执行的地址替换成修复的地址;当__copy_user_zeroing()从用户空间拷贝时可能发生问题,那它就应该负责在其可能发生的问题的指令建立这样的数据结构;

(7)在__generic_copy_from_user()中返回的是为拷贝完的数据大小n,因为__copy_user_zeroing()是个宏不是函数;

(8)CPU从具体系统调用的服务程序返回时,由服务器程序准备好的返回值在寄存器%eax,system_call中将它写到了堆栈对应的位置,这样在RESTORE_ALL,这个值就可通过%eax传回到用户空间;最后到达了ret_from_sys_call中,在ret_from_sys_call中的RESTORE_ALL也是需要修复三条指令;

(9)所有系统调用,从内核入口system_call到进入sys_sethostname的代码都是一样的,以及从sys_sethostname到RESTORE_ALL的iret的代码也是共用的,只有中间的逻辑不一样;

(10)从__copy_user_zeroing()可以看出,内核可以直接访问当前进程的用户空间,所使用的虚拟地址是与当前进程位于用户空间的地址是完全相同的;当然反过来就不可以了;

0 0
原创粉丝点击