版权信息:可以任意转载, 转载时请务必以超链接形式标明文章原文出处,谢谢
原文出处: http://libiao.appspot.com/2009/08/freebsd_system_call.html
系统调用是userland的应用程序调用kernel的接口,其实际上就是一个trap(注: trap是同步的,而interrupt是异步的),可以通过INT 0x80进入内核,具体参见i386/i386/Exception.s这个汇编文件。尽管叫做INT 0x80,其实际上时TGT(Trap Gate),而不是IGT(Interrupt Gate),因此,在进行系统调用的时候,中断还是打开的
下面是进入内核的时候三个步骤:
首先,硬件方面切换到内核模式,因此可以执行内核的代码,有着相应的内核方面的权限
然后,硬件实现方面的将program counter和program status longword以及有关trap的信息等压栈,放到每个进程对应的内核堆栈中(per-process kernel stack)
最后,是使用汇编语言写的将硬件方面没有保存的信息进行保存到内核堆栈中,包括通用寄存器(general-purpose registers),用户堆栈指针(user stack pointer)
在系统调用完成之后,也进行相反的三个步骤:
首先是由汇编语言来恢复通用寄存器、用户堆栈指针等信息
然后是由硬件来恢复program counter以及program status longword
最后是硬件方面切换到用户模式,这样不能够执行有权限的代码
在进行系统调用的时候,需要寄存器来保存参数以及返回值。在调用时,eax是保存系统调用的编号,而ebx,ecx,edx,esi,edi等保存调用参数,返回的时候,使用eax保存返回值(若有返回值的话),并且设置全局变量errno来保存出错的信息。从上面来看,就知道为啥在系统调用中的参数使用的是register_t类型,因为需要保存在寄存器中的
一般errno出错,有两个方面的原因,第一是真的是内核代码发现了错误,没法执行了才报出来;第二就是被中断了,因此,在编写代码的时候,加入信号处理的时候,必须需要知道系统调用在被中断的情况下是否是自动重启还是返回EINTR(interrupted system call)值。
系统调用是可以被信号中断的,当然可以在需要系统调用的时候,设置被中断自动重启,即设置struct sigaction act的参数act.sa_flags |= SA_RESTART;(注意,信号只是软中断,而不是真正意义上的中断,不是异步的,其不打断任何的程序,包括内核程序以及用户程序。其仅仅是在进程上面设置一个标志而已,然后在进程退出的时候才有机会去检查该标志,然后执行相应的信号处理程序)
所有的Syscall都是定义如下:(文件 kern/Init_sysent.c)
struct sysent sysent[] = {
{ 0, (sy_call_t *)nosys, AUE_NULL, NULL, 0, 0, 0 }, /* 0 = syscall */
{ AS(sys_exit_args), (sy_call_t *)sys_exit, AUE_EXIT, NULL, 0, 0, 0 }, /* 1 = exit */
……
{ AS(lpathconf_args), (sy_call_t *)lpathconf, AUE_LPATHCONF, NULL, 0, 0, 0 }, /* 513 = lpathconf */
};
从上面可以看出来,sysent是struct sysent的数组,而struct sysent结构体为:(文件sys/Sysent.h)
struct sysent { /* system call table */
int sy_narg; /* number of arguments */
sy_call_t *sy_call; /* implementing function */
au_event_t sy_auevent; /* audit event associated with syscall */
systrace_args_func_t sy_systrace_args_func;
/* optional argument conversion function. */
u_int32_t sy_entry; /* DTrace entry ID for systrace. */
u_int32_t sy_return; /* DTrace return ID for systrace. */
u_int32_t sy_flags; /* General flags for system calls. */
};
在上述结构体中,最重要的是类型sy_call_t,其定义是:(文件sys/Sysent.h)
typedef int sy_call_t(struct thread *, void *);
特别需要说明的是,对于sy_call_t带的第二个参数,一般情况下是一个结构,包括不同数目的参数,但是需要保证每个结构是register_t类型的整数倍,在i386体系结构中,register_t类型就是整数型
永久链接: http://libiao.appspot.com/2009/08/freebsd_system_call.html