Linux中的系统调用是如何实现的?
Posted
技术标签:
【中文标题】Linux中的系统调用是如何实现的?【英文标题】:How is the system call in Linux implemented? 【发布时间】:2010-10-04 16:37:47 【问题描述】:当我在用户模式下调用系统调用时,调用在 OS 中是如何处理的?
它是调用一些可执行的二进制文件还是一些标准库?
如果是,完成通话需要什么样的东西?
【问题讨论】:
【参考方案1】:看看this。
从 2.5 版开始,linux 内核引入了一个新的系统调用 Pentium II+ 上的进入机制 处理器。由于性能问题 奔腾 IV 处理器与现有的 软件中断方法,一个 替代系统调用条目 机制是使用实现的 SYSENTER/SYSEXIT 指令 Pentium II+ 处理器上可用。 本文探讨了这一新 机制。讨论仅限于 x86 架构和所有源代码 清单基于 linux 内核 2.6.15.6.
-
什么是系统调用?
在系统调用中会发生什么?系统调用提供用户空间 处理请求服务的方式 从内核。什么样的 服务?管理的服务 通过操作系统(如存储), 内存、网络、进程管理 等等例如,如果一个用户进程 想要读取一个文件,它必须 进行“打开”和“读取”系统调用。 一般不调用系统调用 直接通过进程。 C 库 为所有系统提供接口 来电。
好老办法内核代码 sn-p 运行在 用户进程的请求。这段代码 在环 0 中运行(具有当前权限 级别-CPL- 0),这是最高的 x86 中的特权级别 建筑学。所有用户进程运行 在环 3 (CPL 3) 中。
所以,要实现系统调用机制,我们需要的是
1) 一种从环 3 调用环 0 代码的方法。
2) 一些内核代码来服务请求。
新的闪亮方式直到前段时间,linux 曾经 在所有 x86 上实现系统调用 使用软件中断的平台。 要执行系统调用,用户进程 将复制所需的系统调用号 到 %eax 并将执行 'int 0x80'。 这将产生中断 0x80 和 中断服务程序将是 叫。对于中断 0x80,这 例程是一个“所有系统调用 处理“例程。这个例程将 在环 0 中执行。这个例程,如 在文件中定义 /usr/src/linux/arch/i386/kernel/entry.S, 将保存当前状态并调用 基于适当的系统调用处理程序 关于 %eax 中的值。
发现这个软件 中断方法要慢得多 奔腾 IV 处理器。为了解决这个 问题,Linus 实现了一个 替代系统调用机制 利用 SYSENTER/SYSEXIT 所有奔腾提供的说明 II+ 处理器。在更进一步之前 用这种新的方式,让我们 让自己更熟悉 这些说明。
【讨论】:
【参考方案2】:这取决于系统调用的含义。您是指 C 库调用(通过 glibc)还是实际的系统调用? C 库调用最终总是使用系统调用。
进行系统调用的旧方法是通过软件中断,即int
指令。 Windows 有 int 0x2e
,而 Linux 有 int 0x80
。操作系统为中断描述符表 (IDT) 中的 0x2e 或 0x80 设置中断处理程序。这个处理程序然后执行系统调用。它将参数从用户模式复制到内核模式(这由特定于操作系统的约定控制)。在 Linux 上,使用 ebx
、ecx
、edx
、esi
和 edi
传递参数。在 Windows 上,参数是从堆栈中复制的。然后处理程序执行某种查找(以查找函数的地址)并执行系统调用。系统调用完成后,iret
指令返回用户态。
新方法是sysenter
和sysexit
。这两条指令基本上为您完成了所有的注册工作。操作系统通过模型特定寄存器 (MSR) 设置指令。之后它实际上与使用int
相同。
【讨论】:
"C 库调用最终总是使用系统调用。" -- 对此有任何引用吗? @MywikiWitwiki 不是任何 C 库调用,而是对read
、write
等的调用。它们可能不存在的唯一原因——如果它们甚至存在的话——是由于极其专业的优化。您可以假设它们总是跨入内核模式,除非您是具有创新见解的内核专家。【参考方案3】:
它通过 glibc,在用参数填充寄存器后发出 0x80 中断。然后内核的中断处理程序在系统调用表中查找系统调用并调用相关的 sys_*() 函数。
【讨论】:
【参考方案4】:大大简化了,但是当您尝试访问保留的内存地址时会发生中断。中断将上下文切换到内核模式并代表用户执行内核代码(实际系统调用)。调用完成后,控制权返回给用户代码。
【讨论】:
内核代码是什么样的,可执行二进制文件、程序集还是动态链接库? 内核是你系统上正在运行的内核,即内存中的操作系统映像。【参考方案5】:汇编中的 int X 转换为系统调用号 n。 Ex read 系统调用可能会被赋予数字 4。 在系统启动时,操作系统会构建一个称为中断描述符表 (IDT) 的指针表,其中包含系统调用的地址列表以及执行它们所需的特权。 当前特权级别(CPL)保存在 CS 寄存器的一位中(技术上 x86 上为 2 位)。 以下是 int 指令所遵循的步骤: • 从 IDT 中获取第 n 个描述符,其中 n 是 int 的参数。 • 检查 %cs 中的 CPL 是否为 • 如果没有,则用户没有足够的权限执行此操作,将导致执行 int 13 指令(一般保护错误),(用户没有足够的权限) • 如果是,则用户代码有足够的权限执行此系统调用,当前执行上下文被保存(寄存器等),因为我们现在切换到内核模式。 这些信息包括寄存器、标志,因为当系统调用完成时,我们希望从离开的地方继续执行。 • 系统调用的参数保存在内核堆栈中,因为系统调用是在内核模式下执行的。
VSYSCALL(快速系统调用) 每次用户执行系统调用时,操作系统都会保存机器的当前状态(即寄存器、堆栈指针等)并切换到内核模式执行。对于某些系统调用,不必保存所有寄存器。 Ex gettime of day 系统调用读取当前时间,系统调用返回。所以一些系统调用是通过所谓的 vsyscalls 实现的。在这里,当进行系统调用时,它会在用户空间本身中执行,而无需切换到内核。这样就节省了时间。 有关 vsyscall 的详细信息,请参见此处http://www.trilithium.com/johan/2005/08/linux-gate/ 在这里Anyone can understand how gettimeofday works?
【讨论】:
【参考方案6】:系统调用由特殊的陷阱指令、系统调用号和参数组成。
-
特殊的陷阱指令用于从用户模式切换到
具有无限权限的内核模式。
系统调用号和参数通过寄存器传递。
【讨论】:
以上是关于Linux中的系统调用是如何实现的?的主要内容,如果未能解决你的问题,请参考以下文章