本系列文章由张同浩编写,转载请注明出处:http://blog.csdn.net/muge0913/article/details/7518568
邮箱:muge0913@sina.com
过程分析:
1、系统调用需要一个用户空间到内核空间的转换,不同的平台有不同的指令来完成这样的转换,这个指令也叫做操作系统陷入(operating systemtrap)指令。在linux中对于x86来说是用软中断0x80,也即是int $0x80。软中断由软件指令触发,硬中断由硬件触发。
通过软中断,系统会跳到一个预定的内核空间。它指向了系统调用处理程序(不是系统调用服务程序)system_call函数(arch/x86/kernel/entry32.h)。如上图。
2、system_call到服务程序
显然所有的系统调用都会跳到这个地址执行system_call函数。在执行int 0x80时系统调用号会被放入eax寄存器中。因为sys_call_table每个项占用4个字节。所以sys_call_table作为基地址,eax*4作为偏移量就可以找到对应的服务程序的地址。
系统调用的参数通过其他寄存器来传递。如
- write(unsignedint fd,const char *buf,size_t count)
write(unsignedint fd,const char *buf,size_t count)
寄存器ebx,ecx,esi,edx来传递。但是前面我们说过,asmlinkage表示内核从堆栈中提取参数,而不是寄存器。因为在system_call执行时首先把这些寄存器压入堆栈了。从下面的代码中就可找到答案~~~
- ENTRY(system_call)
- RING0_INT_FRAME # can't unwind into user spaceanyway
- pushl_cfi%eax # save orig_eax
- SAVE_ALL
- GET_THREAD_INFO(%ebp)
- #system call tracing in operation / emulation
- testl$_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
- jnzsyscall_trace_entry
- cmpl$(nr_syscalls), %eax
- jaesyscall_badsys
- syscall_call:
- call*sys_call_table(,%eax,4)
ENTRY(system_call)
RING0_INT_FRAME # can't unwind into user spaceanyway
pushl_cfi%eax # save orig_eax
SAVE_ALL
GET_THREAD_INFO(%ebp)
#system call tracing in operation / emulation
testl$_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
jnzsyscall_trace_entry
cmpl$(nr_syscalls), %eax
jaesyscall_badsys
syscall_call:
call*sys_call_table(,%eax,4)
系统服务程序从堆栈中获取参数,并修改,最后再通过堆栈返回修改后的数值。
不是所有的系统调用都有实际内容,如sys_ni_syscll在kernel/sys_ni.c中定义:
- asmlinkage long sys_ni_syscall(void){
- return -ENOSYS;
- }
asmlinkage long sys_ni_syscall(void){
return -ENOSYS;
}
你会发现在sys_call_table中sys_ni_syscall占据了很多内容,其实它代表着已被淘汰的系统调用。
- .longsys_ni_syscall /* old stty syscallholder */
- .longsys_ni_syscall /* old gtty syscallholder */
- .longsys_access
- .longsys_nice
- .longsys_ni_syscall /* 35 - old ftime syscallholder */
- .longsys_sync
- .longsys_kill
- .longsys_rename
- .longsys_mkdir
- .longsys_rmdir /* 40 */
- .longsys_dup
- .longsys_pipe
- .longsys_times
- .longsys_ni_syscall /* old prof syscallholder */
.longsys_ni_syscall /* old stty syscallholder */
.longsys_ni_syscall /* old gtty syscallholder */
.longsys_access
.longsys_nice
.longsys_ni_syscall /* 35 - old ftime syscallholder */
.longsys_sync
.longsys_kill
.longsys_rename
.longsys_mkdir
.longsys_rmdir /* 40 */
.longsys_dup
.longsys_pipe
.longsys_times
.longsys_ni_syscall /* old prof syscallholder */
如上面可知sys_ni_syscall代替了不用的stty和gtty和prof。其实只要是被内核淘汰的系统调用都会被sys_ni_systcall代替。之所以这样是为了老的程序在新的内核上运行时不至于出现大的问题。如不应调用这个系统调用却调用了那个系统调用了。