系统调用是CPU主动地、同步地进入系统空间的手段。
中断有可能发生在CPU已经运行在系统空间的时候,而系统调用却只发生于用户空间。CPU在穿过陷阱门进入系统内核时并不自动关中断,所以系统调用的过程是可中断的。
Linux内核在系统调用时是通过寄存器而不是通过堆栈传递参数的。
发生系统调用,首先检验当前的准入等级与CPU当前的运行等级。然后通过函数指针找到system_call()。
system_call():首先是将寄存器%eax的内容压入堆栈,系统堆栈中这个位置在代码中称为orgi_ax,用于存放系统调用号。系统调用中可以用寄存器来进行传递独立参数。独立参数不能超过5个,且与压入的次序有关。
宏调用GET_CURRENT(%ebx)使寄存器%ebx指向当前进程的task_struct结构。然后检验寄存器%eax中的系统调用号是否超出了范围。在task_struct数据结构中有个成分flags,其中有个标志位叫PT_TRACESYS。一个进程可以通过系统调用ptrace(),将一个子进程的PT_TRACESYS标志位设成1,从而跟踪该子程序的系统调用。
系统调用时,当这个操作的源在用户空间,而目标在系统空间时,要通过一个宏操作copy_from_user( )来完成复制。
对于bad_area,当异常发生于当CPU运行在系统空间的时候,虽然访问失败的目标地址在空户空间中,但CPU的“执行地址”却是在系统空间中。如果内核能够在一个“异常表”中找到发生异常的指令所在的地址,并得到相应的“修复”地址fixup,就将CPU在异常返回后将要重新执行的地址替换成这个“修复”地址。
copy_from_user() 》 _ _copy_user_zeroing( )
在执行的过程中,n随着复制而减小,一直到0为止。如果中途失败,则n表示剩下未完成的大小。
每当装入一个段寄存器时,CPU都要根据这新的段选择码以及GDTR或LDTR的内核在相应的段描述符表中找到所选的段描述项。如果因为不管什么原因而使选择码或描述项无效或不符时,CPU就会产生一次“全面保护”异常(GP异常),当这样的异常发生在系统空间,将空选择码(0)装入一个段寄存器(除CS和SS以外)本身不会引起GP异常,而要到以后企图通过空选择码访问内存时才会引起异常(但那是回到用户空间的事)。当发生GP问题,对于CS和SS不能通过空选择码解决。只好通过do-exit( ),把当前进程杀掉,把当前进程杀了以后,内核会调度另一个进程成为的当前进程。
/从内核中可以直接访问当前进程的用户空间,所使用的虚拟地址也与当进程处于用户空间时的地址完全相同。