|
FreeBSD 5内核源代码分析之系统调用过程
|
|
注:由于code是BBCode的关键字,在某些地方将程序中的变量code改写为_code
系统调用开始于用户程序,接着到达libc进行参数的包装,然后调用内核提供的机制进入内核。
内核提供的系统调用进入内核的方式有几种,包括lcall $X, y方式和
int 0x80方式。其实现都在sys/i386/i386/exception.s中。
我们看最常见的int 0x80入口。
1,int 0x80中断向量的初始化。
------------------
在i386CPU的初始化过程中,会调用函数init386() /*XXX*/
其中有:
| 代码: |
(sys/i386/i386/machdep.c)
-----------------------------------
setidt(IDT_SYSCALL, &IDTVEC(int0x80_syscall), SDT_SYS386TGT, SEL_UPL,
GSEL(GCODE_SEL, SEL_KPL));
-----------------------------------
|
在这里设置好int80的中断向量表。
| 代码: |
(sys/i386/include/segments.h)
---------------------------------
#define IDT_SYSCALL 0x80 /* System Call Interrupt Vector */
#define SDT_SYS386TGT 15 /* system 386 trap gate */
#define SEL_UPL 3 /* user priority level */
#define GSEL(s,r) (((s)<<3) | r) /* a global selector */
#define GCODE_SEL 1 /* Kernel Code Descriptor */
#define SEL_KPL 0 /* kernel priority level */
----------------------------------
|
| 代码: |
(sys/i386/i386/machdep.c)
-----------------------------------
void
setidt(idx, func, typ, dpl, selec)
int idx;
inthand_t *func;
int typ;
int dpl;
int selec;
{
struct gate_descriptor *ip;
ip = idt + idx;
ip->gd_looffset = (int)func;
ip->gd_selector = selec;
ip->gd_stkcpy = 0;
ip->gd_xx = 0;
ip->gd_type = typ;
ip->gd_dpl = dpl;
ip->gd_p = 1;
ip->gd_hioffset = ((int)func)>>16 ;
}
------------------------------------
|
2,int0x80_syscall
------------------
系统调用的入口是int0x80_syscall,在sys/i386/i386/exception.s中。
它其实是一个包装函数,用汇编写成,其目的是为调用C函数syscall()做准备。
| 代码: |
void
syscall(frame)
struct trapframe frame;
|
由于系统调用最终是要调用syscall()这个函数,
因此需要为它准备一个调用栈,包括参数frame,其类型为struct trapframe
| 代码: |
/*
* Exception/Trap Stack Frame
*/
struct trapframe {
int tf_fs;
int tf_es;
int tf_ds;
int tf_edi;
int tf_esi;
int tf_ebp;
int tf_isp;
int tf_ebx;
int tf_edx;
int tf_ecx;
int tf_eax;
int tf_trapno;
/* below portion defined in 386 hardware */
int tf_err;
int tf_eip;
int tf_cs;
int tf_eflags;
/* below only when crossing rings (e.g. user to kernel) */
int tf_esp;
int tf_ss;
};
|
这个trapframe实际上就是保存在核心栈上的用户态寄存器的状态,当从
系统调用返回时,需要从这里恢复寄存器等上下文内容。同时,它又是
函数syscall()的参数,这样在syscall()函数里面就可以方便地操纵返回后
的用户进程上下文状态。
我们来看具体的int0x80_syscall。
| 代码: |
/*
* Call gate entry for FreeBSD ELF and Linux/NetBSD syscall (int 0x80)
*
* Even though the name says 'int0x80', this is actually a TGT (trap gate)
* rather then an IGT (interrupt gate). Thus interrupts are enabled on
* entry just as they are for a normal syscall.
*/
SUPERALIGN_TEXT
IDTVEC(int0x80_syscall)
pushl $2 /* sizeof "int 0x80" */
|
对照struct trapframe可知,此句赋值frame->tf_err=2,记录int 0x80指令的长度,
因为有可能系统调用需要重新执行(系统调用返回ERESTART的话内核会自动重新执行),
需要%eip的值减去int 0x80的指令长度。
| 大部分文章摘自网上,如有侵犯您的权益请与我们联系,我们会第一时间进行处理,谢谢! |
[ 打印文章 ] [ 关闭窗口 ] |
|