新客网WWW.XKER.COM:致力做中国最专业的网络学院!
学院: 操作系统 - 网络应用 - 服务器 - 网络安全 - 工具软件 - 办公软件 - Web开发 - 数据库 - 网页设计 - 图形图像 - 媒体动画 - 硬件学堂 - 存储频道 - QQ专区
您的位置:首页 > 网络学院 > 操作系统 > Unix系统 > FreeBSD教程 > 正文:FreeBSD 5内核源代码分析之系统调用

FreeBSD 5内核源代码分析之系统调用

新客网 XKER.COM 2008-01-19 来源:新客网编辑整理 佚名 收藏本文

假如该进程没有自己的系统调用预备函数,即缺省情况,则根据系统调用是32位还是64位,得到相应的具体系统号,并相应调整指向用户参数的指针。

SYS_syscall对应32位方式,
SYS___syscall对应64位方式。

函数fuword()意为fetch user word,即从用户空间拷贝一个word到内核空间来。其定义在
sys/i386/i386/support.s中,其实现与copyin()类似,我们略过。

此时,具体的系统调用号已经在变量code中了。

代码:
if (p->p_sysent->sv_mask)
code &= p->p_sysent->sv_mask;

对系统调用号做一些调整和限制。

代码:
if ( code >= p->p_sysent->sv_size)
callp = &p->p_sysent->sv_table[0];
else
callp = &p->p_sysent->sv_table[_code];

得到系统调用的函数入口。

代码:
narg = callp->sy_narg & SYF_ARGMASK;

得到该系统调用的参数个数。

代码:
/*
* copyin and the ktrsyscall()/ktrsysret() code is MP-aware
*/
if (params != NULL && narg != 0)
error = copyin(params, (caddr_t)args,
(u_int)(narg * sizeof(int)));
else
error = 0;

将参数从用户态拷贝到内核态的args中。

代码:
#ifdef KTRACE
if (KTRPOINT(td, KTR_SYSCALL))
ktrsyscall(code, narg, args);
#endif

/*
* Try to run the syscall without Giant if the syscall
* is MP safe.
*/
if ((callp->sy_narg & SYF_MPSAFE) == 0)
mtx_lock(&Giant);

假如该系统调用不是MP安全的,则获取全局锁。

代码:

if (error == 0) {
td->td_retval[0] = 0;
td->td_retval[1] = frame.tf_edx;

STOPEVENT(p, S_SCE, narg);

PTRACESTOP_SC(p, td, S_PT_SCE);

error = (*callp->sy_call)(td, args);
}

调用具体的系统调用。
这里,之所以要间接地使用一个系统调用函数表,是因为模拟其他操作系统的需要。同一个系统调用在不同的操作系统里"系统调用号"是不同的,当运行其他操作系统的应用程序时,因为其编译结果是用其他操作系统的"系统调用号",此时需要转换到相应的FreeBSD的"系统调用号"上来,使用系统调用函数表就可以方便地作到这一点。

代码:
switch (error) {
case 0:
frame.tf_eax = td->td_retval[0];
frame.tf_edx = td->td_retval[1];
frame.tf_eflags &= ~PSL_C;
break;

Great,调用成功,设置返回值,并清除carry bit,用户态的libc要根据carry bit 判定系统调用是否成功。

代码:
case ERESTART:
/*
* Reconstruct pc, assuming lcall $X,y is 7 bytes,
* int 0x80 is 2 bytes. We saved this in tf_err.
*/
frame.tf_eip -= frame.tf_err;
break;

系统调用返回ERESTART,内核要尝试重新执行系统调用,因此需要将返回用户空间后的%eip后退,具体后退几个字节,跟系统调用的进入方式有关,假如是通过int 0x80进入的,由于int 0x80指令的长度为两个字节,因此回退2字节,假如是通过lcall $X,y方式进入内核的,由于lcall $X,y指令的长度为7个字节,因此回退7字节。具体几个字节,在刚进入时已经压到堆栈上了(前述pushl $2即是)。

代码:
case EJUSTRETURN:
break;

default:
if (p->p_sysent->sv_errsize) {
if (error >= p->p_sysent->sv_errsize)
error = -1; /* XXX */
else
error = p->p_sysent->sv_errtbl[error];
}
frame.tf_eax = error;
frame.tf_eflags |= PSL_C;
break;
}

假如系统调用返回其他错误的话,则在进程的一个错误对应表中转换错误号。并设置carry bit,以便libc知道。

代码:
/*
* Release Giant if we previously set it.
*/
if ((callp->sy_narg & SYF_MPSAFE) == 0)
mtx_unlock(&Giant);

释放全局锁。

代码:
/*
* Traced syscall.
*/
if ((orig_tf_eflags & PSL_T) && !(orig_tf_eflags & PSL_VM)) {
frame.tf_eflags &= ~PSL_T;
trapsignal(td, SIGTRAP, 0);
}

处理Traced系统调用。

代码:
/*
* Handle reschedule and other end-of-syscall issues
*/
userret(td, &frame, sticks);

做一些调度处理等,后面另分析。

代码:
#ifdef KTRACE
if (KTRPOINT(td, KTR_SYSRET))
ktrsysret(code, error, td->td_retval[0]);
#endif

/*
* This works because errno is findable through the
* register set. If we ever support an emulation where this
* is not the case, this code will need to be revisited.
*/
STOPEVENT(p, S_SCX, code);

PTRACESTOP_SC(p, td, S_PT_SCX);

#ifdef DIAGNOSTIC
cred_free_thread(td);
#endif
W99vNESS_WARN(WARN_PANIC, NULL, "System call %s returning",
(code >= 0 && code < SYS_MAXSYSCALL) ? syscallnames[_code] : "???");
mtx_assert(&sched_lock, MA_NOTOWNED);
mtx_assert(&Giant, MA_NOTOWNED);
}

4, userret()函数
-----------------

简要地看一下userret()函数。
代码:
/*
* Define the code needed before returning to user mode, for
* trap and syscall.
*
* MPSAFE
*/
void
userret(td, frame, oticks)
struct thread *td;
struct trapframe *frame;
u_int oticks;
{
struct proc *p = td->td_proc;

CTR3(KTR_SYSC, "userret: thread %p (pid %d, %s)", td, p->p_pid,
p->p_comm);
#ifdef INVARIANTS
/* Check that we called signotify() enough. */
PROC_LOCK(p);
mtx_lock_spin(&sched_lock);
if (SIGPENDING(td) && ((td->td_flags & TDF_NEEDSIGCHK) == 0 ||
(td->td_flags & TDF_ASTPENDING) == 0))
printf("failed to set signal flags properly for ast()\n");
mtx_unlock_spin(&sched_lock);
PROC_UNLOCK(p);
#endif

/*
* Let the scheduler adjust our priority etc.
*/
sched_userret(td);

调度器处理。

代码:
/*
* We need to check to see if we have to exit or wait due to a
* single threading requirement or some other STOP condition.
* Don't bother doing all the work if the stop bits are not set
* at this time.. If we miss it, we miss it.. no big deal.
*/
if (P_SHOULDSTOP(p)) {
PROC_LOCK(p);
thread_suspend_check(0); /* Can suspend or kill */
PROC_UNLOCK(p);
}

是否需要停住?系统的某些时候只答应单个线程运行。

代码:
/*
* Do special thread processing, e.g. upcall tweaking and such.
*/
if (p->p_flag & P_SA) {
thread_userret(td, frame);
}

又是scheduler activation的东西,通知用户态的thread manager。
(FIXME)

代码:
/*
* Charge system time if profiling.
*/
if (p->p_flag & P_PROFIL) {
quad_t ticks;

mtx_lock_spin(&sched_lock);
ticks = td->td_sticks - oticks;
mtx_unlock_spin(&sched_lock);
addupc_task(td, TRAPF_PC(frame), (u_int)ticks * psratio);
}
}

最后是profiling的东西。

共2页: 上一页 [1] [2] 下一页
收藏】 【评论】 【推荐】 【投稿】 【打印】 【关闭
发表评论
要记得去论坛讨论,点击注册新会员匿名评论
评论内容:不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。
阅读排行
随机推荐
实用信息推荐