Linux内核如何"监听"C库?

now*_*wat 1 c system-calls linux-kernel userspace

我正试图建立一个关于Linux内核和用户空间如何工作的"大图",我很困惑.我知道用户空间利用系统调用与内核"交谈",但我不知道如何.我试图阅读C库和内核源代码,但它们很复杂且不易理解.我还阅读了几本关于操作系统概念事实的书籍,比如管理进程,内存,设备,但它们没有明确"转换"(用户空间 - >内核).那么,用户空间和内核空间之间的转换到底发生了什么?C库如何运行在机器中运行的Linux内核中的代码?

打个比方:假设有一所房子.房子被锁了.打开房子的关键在于房子内部.屋内只有一个人,内核.用户空间是有人试图进入房子.我的问题是:内核如何知道房子外面有人想要密钥,哪个机制允许用该密钥打开房子?

Krz*_*ski 10

这很安静 - 这个人可以用门铃让内核知道它在外面等着.在我们的例子中,这个门铃通常是一个特殊的CPU异常,软件中断或允许用户空间使用并且内核可以处理的专用指令.

所以程序是这样的:

  1. 首先,您需要知道系统呼叫号码.每个系统调用都有它唯一的编号,内核中有一个表将这些数字映射到特定的函数.每个体系结构都有不同的表,在两个不同的体系结构上可以映射不同的系统调用.
  2. 然后你设置你的论点.这也是特定于体系结构的,但与在常规函数调用之间传递参数没有太大区别.通常,您将把参数放在特定的CPU寄存器中.这在该体系结构的ABI中描述.
  3. 然后输入系统调用.根据体系结构,这可能意味着导致某些异常或使用专用CPU指令.
  4. 内核具有特殊的处理函数,在调用syscall时以内核模式运行.它将暂停进程执行,存储特定于此进程的所有信息(这称为context switch),读取系统调用号和参数并调用正确的系统调用例程.它还将确保将返回值放在适当的位置以供用户空间读取,并在syscall例程完成时调度进程(恢复其上下文).

例如,要让内核知道你想在x86_64上调用syscall,你可以sysenter%rax寄存器中使用带有系统调用号的指令.参数可通过寄存器(如果我没记错)通过%rdi,%rsi,%rdx,%rcx,%r8%r9.

您还可以使用32位x86 CPU上使用的旧方法 - 软件中断号0x80(int 0x80指令).再次,系统调用号在指定%rax的寄存器和参数去(同样,如果我没有记错)%ebx,%ecx,%edx,%esi,%edi,%ebp.

ARM非常相似 - 您将使用"supervisor call"指令(SVC #0).您的系统调用号将进入r7注册,所有参数将转到寄存器r0-r6,系统调用的返回值将存储在r0.

其他架构和操作系统使用类似的技术.细节可能会有所不同 - 软件中断号可能不同,参数可能使用不同的寄存器传递,甚至使用堆栈,但核心思想是相同的.