我正在查看一些Android源代码,发现writev
的实现完全在汇编中。我对ASM有一般的工作知识,但不熟悉ARM指令集的细微差别。考虑这段代码:
ENTRY(writev)
mov ip, r7
ldr r7, =__NR_writev
swi #0
mov r7, ip
cmn r0, #(MAX_ERRNO + 1)
bxls lr
neg r0, r0
b __set_errno
END(writev)
经过一番搜索,我发现了以下内容:
__NR_writev = __NR_SYSCALL_BASE+146
__NR_SYSCALL_BASE = 0
这到底发生了什么?根据我的发现,ip
寄存器是r12的别名,并且还称为临时寄存器。我在源代码中找到的几乎所有ASM实现都有这样的总体布局:
代码将r7加载到ip中,r7加载了例程的实际内存地址,然后发出软件中断。虽然我不确切知道发生了什么,但我可以看到剩下的代码是使用__NR_writev子程序调用的返回值来检查错误。
如果此ASM例程仅重定向调用并检查错误,那么writev的实际实现在哪里?
为什么r7被保存在这里以及为什么要进入ip(r12 / scratch register)?
为什么实际实现的内存地址在SWI之前加载到r7?
答案 0 :(得分:3)
writev的实际实现位于操作系统内核中。 SWI指令使CPU切换到管理程序模式,并开始执行内核的异常处理程序。在管理员模式下,内核可以执行用户模式代码无法直接执行的操作,例如访问设备。
R7的值保存到IP(R12),因为遵循的调用约定要求在整个调用中保留R7的值。它不需要保存IP寄存器。
加载到R7中的值不是地址,而是数字146.由于其他系统调用也使用SWI #0
,它们最终都会在内核中执行相同的异常处理程序。 R7中的值用于确定调用哪个系统调用。异常处理程序可能会跳转到内核中的系统调用实现,方法是将R7中的值用作地址表中的缩放偏移量。