Max*_*kyi 3 c linux assembly operating-system x86-64
我试图理解C语言系统调用API,syscall汇编程序指令和用于在进程之间切换上下文的异常机制(中断)之间的关系.我自己要研究很多,所以请耐心等待.
我的理解是正确的,C语言系统调用是由编译器实现的,syscall在程序集中有相应的代码,而这又是由OS作为异常机制(中断)实现的?
所以write在以下C代码中调用该函数:
#include <unistd.h>
int main(void)
{
write(2, "There was an error writing to standard out\n", 44);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
编译为汇编作为syscall指令:
mov eax,4 ; system call number (sys_write)
syscall
Run Code Online (Sandbox Code Playgroud)
反过来,指令由OS实现为异常机制(中断)?
该syscall指令本身就像一个荣耀的跳跃,这是一个硬件支持的方式,高效,安全地从非特权用户空间跳转到内核.
该syscall指令跳转到内核入口点时调度呼叫.
在x86_64之前,使用了另外两种机制:int指令和sysenter指令.
它们具有不同的入口点(目前仍然存在于32位内核中,以及可以运行32位用户空间程序的64位内核).
前者使用x86中断机制,可以与异常调度混淆(也使用中断机制).
但是,异常是虚假事件,而int用于生成软件中断,同样是一个美化的跳跃.
C语言不关心系统调用,它依赖于C运行时来执行与未来程序环境的所有交互.
C运行时通过特定于环境的机制实现上述交互.
可能存在各种层次的软件抽象,但最终会调用OS API.
术语API用于表示合同,严格来说使用API不需要调用一段内核代码(趋势是在用户空间中实现非关键功能以限制可利用的代码),这里我们只对此感兴趣在需要权限切换的API子集中.
在Linux下,内核公开了一组可从用户空间访问的服务,这些入口点称为系统调用.
在Windows下,内核服务(使用与Linux类似物相同的机制访问)被认为是私有的,因为它们不需要在不同版本之间保持稳定.
一组DLL/EXE导出函数用作入口点(例如ntoskrnl.exe,hal.dll,kernel32.dll,user32.dll),后者又通过(私有)系统调用使用内核服务.
请注意,在Linux下,大多数系统调用都有一个POSIX包装器,因此可以使用这些包装器(即普通的C函数)来调用系统调用.
底层ABI是不同的,错误报告也是如此; 包装器在两个世界之间进行转换.
C运行时调用OS API,在Linux的情况下,系统调用是直接使用的,因为它们是公共的(在某种意义上,在各个版本中是稳定的),而对于Windows,通常的DLL,如kernel32.dll,被标记为依赖项和使用.
我们被简化到一个用户模式程序,它是C运行时(Linux)的一部分或API DLL(Windows)的一部分,需要调用内核中的代码.
x86架构历史上提供了不同的方法,例如,一个呼叫门.
另一种方法是通过int指令,它有一些优点:
int指令是合适的,因为向量数(例如21h)比远地址(例如0f000h:0fff0h)更容易记住.随着架构的现代化,这种机制变得有一个很大的缺点:它很慢.在引入sysenter(注意,sysenter不是syscall)指令之前没有更快的替代方案(调用门同样慢).
与奔腾PRO/II的问世[1]一对新的指令sysenter,并sysexit进行了介绍,使系统调用更快.
Linux从版本2.5开始使用它们,至今仍在32位系统上使用,我相信.
我不会解释sysenter指令的整个机制以及使用它所需的伴随VDSO,只需要说它比int机制更快(我找不到Andy Glew的一篇文章,他说的sysenter结果是在奔腾III上速度慢,我不知道它现在的表现如何).
随着x86-64的出现,AMD对sysenter(即syscall/ sysretpair)的响应开始了从用户模式切换到内核模式的事实上的方式.
这是由于这样的事实,sysenter实际上是快速和非常简单的(它复制 rip和rflags成rcx和r11分别,口罩rflags并跳转到设置在一个地址IA32_LSTAR).
Linux和Windows的64位版本都使用syscall.
总结一下,可以通过三种机制给内核控制:
int 80h适用于32位Linux(2.5之前版)和int 2eh32位Windows. sysenter.syscall.C运行时通常是一个静态库,因此是预编译的,它使用上述三种方法之一.
该syscall指令直接将控制转移到内核入口点(参见entry_64.s).
这是少了点所以,它不是由操作系统执行的指令,它使用的操作系统.
术语异常在CS中重载,C++有异常,Java和C#也是例外.
操作系统可以有一个与语言无关的异常捕获机制(在windows下它曾经被称为SEH,现在已被重写).
CPU也有例外.
我相信我们正在谈论最后的意义.
异常是通过中断发送的,它们是一种中断.
它没有说明,虽然异常是同步的(它们发生在特定的,可重放的点)它们是"不需要的",但它们是特殊的,因为程序员倾向于避免它们,当它们发生时是由于一个bug,一个未处理的角落情况或糟糕的情况.
因此,它们不用于将控制转移到内核(它们可以).
使用软件中断(也是同步的); 机制几乎完全相同(异常可以在内核堆栈上推送状态代码)但语义不同.
我们从不引用空指针,访问未映射的页面或类似于调用系统调用,我们使用了int指令.