一些(很多?全部?)64位 1 Linux发行版允许通过提供32位和64位库(包括libc)的并行集合来运行32位应用程序。因此,32位应用程序可以链接到32位库,并由64位内核运行。
我想知道32位应用程序如何在64位内核上进行系统调用的 mechanics 。我怀疑答案是在libc和/或内核源代码中的某个地方,但是由于我不知道在哪里看,所以潜入源代码会很费时。
还有一个更重要的问题,是否有任何性能开销? 2 逻辑上,来自32位app系统调用的调用必须转换为64位内部内核环境。如何以及在哪里完成?
1 “32位”= IA-32,“64位”= AMD64
2 在你的回答中假设它很重要:)
答案 0 :(得分:27)
从用户空间方面来说,机制与在32位本机内核上进行系统调用相同 - 所有用户模式代码(包括32位glibc)都以相同的方式工作。
从内核端,设置用户空间(例如int 0x80
)的旧IA32入口点以调用ia32_syscall
汇编程序例程。 (转换到内核空间涉及处理器加载内核的代码段选择器,这会导致转换为64位“长”模式)。
ia32_syscall
例程然后将一些参数混合以匹配x86_64系统调用约定:
movl %edi,%r8d
.if \noebp
.else
movl %ebp,%r9d
.endif
xchg %ecx,%esi
movl %ebx,%edi
movl %edx,%edx /* zero extension */
然后使用IA32系统调用号通过表ia32_sys_call_table
进行函数调用。这基本上将IA32系统调用号与本机系统调用实现相匹配(系统调用号在IA32和x86_64之间差别很大)。该表的第一部分如下所示:
ia32_sys_call_table:
.quad sys_restart_syscall
.quad sys_exit
.quad stub32_fork
.quad sys_read
.quad sys_write
对于大多数系统调用,现在可以直接调用x86_64实现 - 如exit()
。对于其他人,如fork()
,提供了一个正确实现预期IA32语义的包装器(特别是,如果需要从32位到64位的参数的符号扩展)。
正如您所看到的,内核代码的开销很小 - 对寄存器值进行了一些微不足道的修改,对于一些函数,还有一个额外的函数调用。我不确定加载导致从32位模式转换到64位模式的代码段选择器对于处理器执行的速度比不执行的速度慢 - 请检查处理器体系结构手册。