【问题标题】:What is better "int 0x80" or "syscall" in 32-bit code on Linux?Linux 上 32 位代码中的“int 0x80”或“syscall”哪个更好?
【发布时间】:2012-09-30 04:06:26
【问题描述】:

我研究了 Linux 内核,发现对于 x86_64 架构,中断int 0x80 不适用于调用系统调用1

对于 i386 架构(32 位 x86 用户空间),更可取的是:syscallint 0x80,为什么?

我使用 Linux 内核版本 3.4。


脚注 1:int 0x80 在某些情况下确实适用于 64 位代码,但从不推荐。 What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?

【问题讨论】:

  • 您在内核中的哪个位置看到int 0x80 的使用?你能指定一些文件吗?
  • @Mike 实际上,我找到了一种关于 linux 内核的教程,并以它为例。它基于 2.6。
  • 我认为int 0x80 直接在x86-64 内核上工作是为了向后兼容。而且Intel手册上说syscall在32位模式下无效。
  • 补充 self: syscall 仅在 Intel 实现中无效,AMD 无效:stackoverflow.com/questions/29783896/…

标签: linux assembly x86 system-calls 32-bit


【解决方案1】:
  • syscall 是在x86-64 上进入内核模式的默认方式。此指令不适用于 32 位操作模式英特尔处理器
  • sysenter 是最常用于在 32 位操作模式下调用系统调用的指令。它类似于syscall,虽然使用起来有点困难,但这是内核关心的问题。
  • int 0x80 是调用系统调用的传统方式,应避免使用。

调用系统调用的首选方法是使用vDSO,这是映射到每个进程地址空间的内存的一部分,允许更有效地使用系统调用(例如,在某些情况下根本不进入内核模式)。与传统的int 0x80 方式相比,vDSO 还处理更困难的syscallsysenter 指令。

另外,请参阅 thisthis

【讨论】:

  • 对于操作系统内核开发人员的建议是二选一。然后他们的选择将成为 ABI 的一部分,如果您正在为某个操作系统进行开发,则必须尊重该 ABI。例如,Linux/i386 使用 int 0x80 并在寄存器中传递参数,而 MirBSD/i386 使用 int 0x80 并在堆栈上传递参数,中间有一个帧指针(这意味着使用 cdecl 时用户空间没有设置成本)。 [这个答案主要是针对那些被送到这里但不是操作系统内核设计者的人。]
  • 问题是关于 Linux 的,答案描述了在 Linux(i386 和 x86_64)上调用系统调用的可能方法。您建议 Linux/i386 仅使用 int 0x80,这是不正确的。进行系统调用的最佳方式是使用 VDSO。
  • 相关:What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?。 (它“有效”,但仅适用于寄存器的低 32 位,因此您不能将其与 64 位指针一起使用。)
  • VDSO 仅适用于 32 位 x86。我认为 x86-64 glibc 直接使用 syscall ABI,除了像 clock_gettime()getpid() 这样在 VDSO 中导出用户空间实现的系统调用。
【解决方案2】:

在更改之前请注意:系统调用号不同在执行 0x80 或 syscall 时,例如 sys_write 是 4 与 0x80 和 1 与 syscall。

http://docs.cs.up.ac.za/programming/asm/derick_tut/syscalls.html 用于 32 位或 0x80 http://blog.rchapman.org/post/36801038863/linux-system-call-table-for-x86-64 用于系统调用

【讨论】:

  • 问题是关于在 32 位模式下使用 syscall 调用 32 位 ABI(仅在 AMD CPU 上可行,并且行为不同于 64 位用户空间版本)。但是,是的,64 位系统调用 ABI 在调用编号和调用约定方面有所不同。
【解决方案3】:

我的回答 here 涵盖了您的问题。

实际上,最近的内核正在实现VDSO,特别是为了动态优化系统调用(内核将 VDSO 设置为一些最适合当前处理器的代码)。所以你应该使用 VDSO,对于现有的系统调用,你最好使用 libc 提供的接口。

请注意,AFAIK,简单系统调用成本的很大一部分是从用户空间到内核并返回。因此,对于某些系统调用(可能是gettimeofdaygetpid ...),VDSO 甚至可能会避免这种情况(从技术上讲,可能会避免进行真正的系统调用)。对于大多数系统调用(如openreadsendmmap ....),系统调用的内核成本足够大,足以改进用户空间到内核空间的转换(例如,使用SYSENTERSYSCALL 机器指令而不是 INT) 无关紧要。

【讨论】:

  • 早在 80386 天,进入内核的最快方式居然是execute an invalid instruction
  • @AdamRosenfield 是 "The hunt for a faster syscall trap" 您链接到的正确博客条目?你的已经烂了……
  • @Ruslan:是的,就是这个。 Raymond Chen 的博客在去年的某个时候经历了一次迁移,很多像这样的旧链接都坏了。
【解决方案4】:

int 0x80 是一个更好的术语,表示它对内核的系统调用告诉它做某事。

含义和解释可以互换,“make a syscall”或“issue int 80h”。

这与 DOS 时代没有什么不同:

  • 调用 int 21h 以使 DOS 执行依赖于 AX 寄存器和可选的 ES:DX 寄存器对的操作,
  • int 13h 是 BIOS 硬盘处理程序。
  • int 10h 是 EGA/VGA 屏幕。
  • int 09h 是键盘处理程序。

这里的共同主题是,当调用中断/系统调用时,内核会检查寄存器的状态以查看需要什么类型的系统调用。例如,通过查看eax 寄存器,并确定要执行的操作,内部上下文切换到内核空间,执行过程并将上下文切换回用户空间,并可以选择返回结果调用,即是成功还是失败。

【讨论】:

  • 其实x86_64指令集有一条syscall指令。在这方面,int 0x80 仅用于 32 位 Linux 环境中的系统调用。
  • 好吧,公平地说,OP 应该更清楚 x86 架构什么是更可取的 syscall 或 int 0x80 - 因为 OP 提到 x86-64 和从那个上下文,暗示 32 位......这让我的回答有点误入歧途或被打断的异常:P
猜你喜欢
  • 2021-12-26
相关资源
最近更新 更多