系统调用
系统调用就是调用操作系统提供的一系列内核功能函数,因为内核总是对用户程序持不信任的态度,一些核心功能不能直接交由用户程序来实现执行。用户程序只能发出请求,然后内核调用相应的内核函数来帮着处理,将结果返回给应用程序。如此才能保证系统的稳定和安全。
系统调用是给用户态下的程序使用的,但是用户程序并不直接使用系统调用,而是系统调用在用户态下的接口。这个用户接口就是操作系统提供的系统调用API,一般遵循POSIX标准。
每一个系统调用都唯一分配了一个整数来标识,通常的做法就是将这个系统调用号放进eax寄存器,当执行到系统调用入口程序的时候就会根据eax的值去调用具体的系统调用程序,比如说eax中存放的是1那么就会去调用fork这个系统调用的相关函数。
系统调用的实现
参数
有些系统调用的是需要参数的,用户接口不真正干活,真正干活的是内核功能函数,但是需要的参数在用户态下,所以需要在用户接口部分向内核传递参数。传参有两种方法:
直接传给寄存器,寄存器是通用的,在用户态将值传给寄存器,进入内核态之后就可以直接使用,这可以使用内联汇编来实现。
压栈,压栈有个问题,系统调用使用中断/陷阱来实现,这期间会换栈,在用户态下压栈的参数对内核来说似乎没什么用处。所以要想使用用户态下栈中的参数,必须要获得用户栈的地址,这个值在哪呢?没错,在内核栈中的上下文保存着,从内核栈中取出用户栈的栈顶esp值,就可以取到系统调用的参数了,xv6就是这样实现的。
返回值
函数的调用约定中规定了返回值应该放在eax寄存器里面。而在系统调用的一开始我们将系统调用号传进了eax寄存器,然后中断时保存上下文,将eax压入内核栈,系统调用处理程序将最后结果放到eax寄存器中。