Syscall - restart或retry的处理

在user利用syscall指令(x86)或svc指令(Arm)发起系统调用后,如果kernel在处理系统调用的过程中被异常打断(如果有信号量pending或被异常唤醒等),此时是retry还是返回?由于是在user的进程上下文中,根据机制和策略分离的原则,kernel一般不会在系统调用处理函数中做retry动作,而是返回user space后再次发起系统调用。不同的操作系统实现方式不一样,我们以Magenta和Linux为例说明。


Magenta的syscall流程


用户空间系统调用接口的实现

我们先来看看magenta的系统调用流程(以x86_64为例)。正如在Magenta-Userboot中所描述,user的系统调用接口实现在libmagenta.so中。通过一些宏定义以及工具,生成了系统调用接口函数。后面假设编译后生成的目录是“build-magenta-pc-x86-64”。

在文件

build-magenta-pc-x86-64/gen/include/magenta/syscalls-x86-64.S

中定义了接口函数,形如:

... 
m_syscall mx_futex_wait 42 3 0
m_syscall mx_futex_wake 43 2 1
...  

其中m_syscall是个宏

.macro m_syscall name, num, nargs, public 
...
.endm

它的参数“public”用来表示是否定义全局的函数名。以mx_futex_wake为例,展开后生成如下函数名:

hidden: SYSCALL_mx_futex_wake  
_mx_futex_wake = SYSCALL_mx_futex_wake
weak: mx_futex_wake = SYSCALL_mx_futex_wake
hidden: VDSO_mx_futex_wake = SYSCALL_mx_futex_wake

而mx_futex_wait 只生成了:

hidden: SYSCALL_mx_futex_wait

为什么要做“public”的区分尼?我们来看看如下文件:

build-magenta-pc-x86-64/gen/include/magenta/syscall_vdso_wrappers.inc

文件的内容形如:

mx_status_t _mx_futex_wait(mx_futex_t* value_ptr, int current_value, mx_time_t deadline) {
    mx_status_t ret;
    do {
        ret = SYSCALL_mx_futex_wait(value_ptr, current_value, deadline);
    } while (unlikely(ret == MX_ERR_INTERRUPTED_RETRY));
    return ret;
}

VDSO_INTERFACE_FUNCTION(mx_futex_wait);

宏VDSO_INTERFACE_FUNCTION生成了如下的函数名:

mx_futex_wait = _mx_futex_wait
VDSO_mx_futex_wait = _mx_futex_wait

从文件syscall_vdso_wrappers.inc来看,定义的函数都是形如:

do {
   ret = XXX;
} while (unlikely(ret == MX_ERR_INTERRUPTED_RETRY));

可见,对于这一类的系统调用,如果返回值是MX_ERR_INTERRUPTED_RETRY,则会继续尝试发起系统调用。

由上分析可见,Magenta的系统调用retry,是在用户的vdso lib中实现的。

kernel系统调用的实现

在kernel层面,系统调用的总入口函数是x86_syscall,根据系统调用号,直接跳转到系统封调用表Lcall_wrapper_table总对应的系统调用函数。Lcall_wrapper_table是由宏 start_syscall_dispatch 和 syscall_dispatch来定义的,可参见文件:

build-magenta-pc-x86-64/gen/include/magenta/syscalls-kernel-branches.S

可见接口函数的定义形如:wrapper_\syscall。 比如 wrapper_futex_wait。这些wrapper函数定义在文件:

build-magenta-pc-x86-64/gen/include/magenta/syscalls-kernel-wrappers.inc

形如:

...
x86_64_syscall_result wrapper_futex_wait(mx_futex_t* value_ptr, int current_value, mx_time_t deadline, uint64_t ip) {
    return do_syscall(MX_SYS_futex_wait, ip, &VDso::ValidSyscallPC::futex_wait, [&]() {
        return static_cast<uint64_t>(sys_futex_wait(make_user_ptr(value_ptr), current_value, deadline));
    });
}
 ...

do_syscall主要是做了权限检查,确认发起系统调用的是否是vdso代码。检查通过后,调用 sys_futex_wait。


Linux的系统调用retry实现

Linux的retry可以分为2大类:kernel处理和user处理。从系统调用的返回值也可见一斑。

#define ERESTARTSYS 512
#define ERESTARTNOINTR  513
#define ERESTARTNOHAND  514

以上的返回值由kernel负责处理,是不会返回给user的。所以对这些返回值有如下要求:
当设置返回值为以上的值时,一定要同步设置TIF_SIGPENDING,即指示signal pending,以便kernel可以进入signal处理函数。

在处理函数中,将用户空间的返回地址做了减2处理。系统调用是利用指令syscall进入kernel的,原来的用户返回地址是syscall后的地址;syscall指令占2个bytes,做了减2处理后,返回地址又指向了syscall指令。从而当返回到用户空间后,又会发起syscall进入系统调用流程。

可见Linux的retry方法和Magenta的不同。

而如下的返回值是返回给用户的,由用户程序决定是否retry还是直接abort。

#define EINTR       4
#define EAGAIN      11
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值