原创作品转载请注明出处
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
系统调用分派表
(dispatch table)存放在
sys_call_table
数组
186.macro SAVE_ALL
187 cld
188 PUSH_GS
189 pushl_cfi %fs
190 /*CFI_REL_OFFSET fs, 0;*/
191 pushl_cfi %es
192 /*CFI_REL_OFFSET es, 0;*/
193 pushl_cfi %ds
194 /*CFI_REL_OFFSET ds, 0;*/
195 pushl_cfi %eax
196 CFI_REL_OFFSET eax, 0
197 pushl_cfi %ebp
198 CFI_REL_OFFSET ebp, 0
199 pushl_cfi %edi
200 CFI_REL_OFFSET edi, 0
201 pushl_cfi %esi
202 CFI_REL_OFFSET esi, 0
203 pushl_cfi %edx
204 CFI_REL_OFFSET edx, 0
205 pushl_cfi %ecx
206 CFI_REL_OFFSET ecx, 0
207 pushl_cfi %ebx
208 CFI_REL_OFFSET ebx, 0
209 movl $(__USER_DS), %edx
210 movl %edx, %ds
211 movl %edx, %es
212 movl $(__KERNEL_PERCPU), %edx
213 movl %edx, %fs
214 SET_KERNEL_GS %edx
215.endm
SAVE_ALL
先保存用户模式的寄存器和堆栈信息,然后切换到内核模式
system_call函数根据用户传来的
系统调用号
,在系统调用表里找到对应的系统调用再执行
//2个参数
#define _syscall2(type,name,type1,arg1,type2,arg2)
//3个参数
#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3)
//4个参数
#define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4)
//5个参数
#define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,type5,arg5)
//6个参数
#define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,type5,arg5,type6,arg6)
int $0x80通过软中断开触发系统调用,当发生调用时,函数中的name会被系统系统调用名所代替。
然后调用前面所讲的system_call。
这个过程里包含了系统调用的初始化,系统调用的初始化原代码在
arch/i386/kernel/traps.c中
838#ifdef CONFIG_X86_32
839 set_system_trap_gate(SYSCALL_VECTOR, &system_call);
840 set_bit(SYSCALL_VECTOR, used_vectors);
841#endif
设置系统调用的中断向量(陷阱门)
# system call handler stub
490ENTRY(system_call)
491 RING0_INT_FRAME # can't unwind into user space anyway
492 ASM_CLAC
493 pushl_cfi %eax # save orig_eax
494 SAVE_ALL
495 GET_THREAD_INFO(%ebp)
496 # system call tracing in operation / emulation
497 testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
498 jnz syscall_trace_entry
499 cmpl $(NR_syscalls), %eax
500 jae syscall_badsys
501syscall_call:
502 call *sys_call_table(,%eax,4)
503syscall_after_call:
504 movl %eax,PT_EAX(%esp) # store the return value
......
</pre><pre name="code" class="cpp">532irq_return:
533 INTERRUPT_RETURN
</pre><pre name="code" class="cpp">......
588ENDPROC(system_call)
每当用户执行int 0x80时,系统进行中断处理,把控制权交给内核的system_call。
整个系统调用的过程可以总结如下:
1. 执行用户程序(如:fork)
2. 根据glibc中的函数实现,取得系统调用号并执行int $0x80产生中断。
3. 进行地址空间的转换和堆栈的切换,执行SAVE_ALL。(进行内核模式)
4. 进行中断处理,根据系统调用表调用内核函数。
5. 执行内核函数。
6. 执行RESTORE_ALL并返回用户模式
解了系统调用的实现及调用过程,我们可以根据自己的需要来对内核的系统调用作修改或添加。