您的位置:首页 > 运维架构 > Linux

系统调用的详细过程

2017-07-09 17:39 148 查看

用户态:

首先先找到系统调用号,我们来看unistd.h头文件中这样一段代码:
#define __NR_restart_syscall      0
#define __NR_exit		  1
#define __NR_fork		  2
#define __NR_read		  3
#define __NR_write		  4
#define __NR_open		  5
#define __NR_close		  6
#define __NR_waitpid		  7
......
#define _syscall0(type,name) \
type name(void) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
: "=a" (__res) \
: "0" (__NR_##name)); \
__syscall_return(type,__res); \
}
在这个头文件中,我们定义了一系列的宏,这些宏就是系统调用对用的系统调用号,比如for,它通过组装将name替换成fork之后进行二次展开就成了_NR_fork,因此在前面定义_NR_fork 就是2,因此fork对应的系统调用号就是2,而系统调用就是通过0x80号中断号找到对应的系统调用服务程序,而真正执行系统调用服务程序时就已经陷入内核态,在此之前我们需要将系统调用的调用号,调用参数进行传递,在陷入内核态前有这样一段汇编代码:(示例代码的系统调用有两个参数)

 
0: 89 da mov %ebx,%edx
2: 8b 4c 24 08 mov 0x8(%esp,1),%ecx
6: 8b 5c 24 04 mov 0x4(%esp,1),%ebx
a: b8 4a 00 00 00 mov $0x4a,%eax
f: cd 80 int $0x80


它首先把两个参数传入寄存器,然后将0x4a号(示例系统调用函数的系统调用号,不同的系统调用会不同,会根据前面得到的系统调用号传入eax)系统调用传入eax寄存器中,最后就调用int陷入内核态。

内核态:

系统调用服务程序:
ENTRY(system_call)
pushl %eax			# save orig_eax
SAVE_ALL
GET_THREAD_INFO(%ebp)
# system call tracing in operation
testb $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT),TI_flags(%ebp)
jnz syscall_trace_entry
cmpl $(nr_syscalls), %eax
jae syscall_badsys
syscall_call:
call *sys_call_table(,%eax,4)
它首先将用户态的一些寄存器信息保存在自己的堆栈上(内核堆栈),save_all就是一个宏,他将依次压入:%es%ds%eax%ebp%edi%esi%edx%ecx
%ebx,而es,ds,eax,ebp均有各自的用处,所以允许传递的最大参数的个数为后面5 个,如果更多就传递指针,通过copy_from_user函数在指针处获取参数 ,用户态信息保存完了,参数也保存了就call具体的系统函数,函数调用跳转表就是保存的系统函数的函数指针,通过系统调用号找到具体的系统函数就开始执行。
系统函数跳转表:
ENTRY(sys_call_table)
.long sys_restart_syscall	/* 0 - old "setup()" system call, used for restarting */
.long sys_exit
.long sys_fork
.long sys_read
.long sys_write
.long sys_open		/* 5 */
.long sys_close
.long sys_waitpid
.long sys_creat
.long sys_link
.long sys_unlink	/* 10 */
.long sys_execve


 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息