返回

Linux从头学13:想彻底搞懂系统调用的底层原理?建议您别错过这篇调用门

闲谈

导语

在之前的文章《Linux从头学10:三级跳过程详解 - 从bootloader到操作系统,再到应用程序》中,我们介绍了操作系统是如何从bootloader加载到内存中并启动的。但是在当时,我们还没有引入特权级的概念,因此用户程序和操作系统都工作在相同的特权级,可以直接通过段选择符来访问内存。

但是在实际的操作系统中,用户程序和操作系统是运行在不同的特权级上的。用户程序运行在用户态,而操作系统运行在内核态。用户态的程序只能访问自己的内存空间,而内核态的程序可以访问整个系统的内存空间。

为了让用户态的程序能够请求操作系统提供服务,就需要一种机制来允许用户态的程序切换到内核态,并执行内核态的代码。这种机制就是系统调用。

系统调用简介

系统调用是一种特殊的函数,它允许用户态的程序请求操作系统提供服务。系统调用的本质是通过一个特定的指令(通常是int 0x80)来触发一个中断,然后操作系统会捕获这个中断并执行相应的服务。

系统调用可以分为两类:

  • 内核调用: 内核调用是直接由操作系统内核提供的服务,例如分配内存、创建进程等。
  • 库调用: 库调用是通过库函数来实现的系统调用,例如printf、scanf等。

调用门机制

调用门是一种特殊的段符,它允许用户态的程序通过特定的指令(通常是int 0x80)来触发一个中断,并跳转到内核态的代码执行。

调用门的结构如下:

struct call_gate {
  unsigned short offset_low;
  unsigned short selector;
  unsigned char count;
  unsigned char access;
  unsigned short offset_high;
};

其中:

  • offset_low:中断处理程序的低16位偏移量。
  • selector:中断处理程序所在的段选择符。
  • count:中断处理程序的长度。
  • access:中断处理程序的访问权限。
  • offset_high:中断处理程序的高16位偏移量。

特权级切换

当用户态的程序触发一个系统调用时,CPU会发生特权级切换,从用户态切换到内核态。特权级切换的过程如下:

  1. CPU将当前的段寄存器值压入堆栈。
  2. CPU将调用门的段选择符装入段寄存器。
  3. CPU将调用门的偏移量装入指令指针寄存器。
  4. CPU执行中断处理程序中的代码。

内核态和用户态之间的交互

内核态和用户态之间的交互主要通过系统调用来实现。用户态的程序可以通过系统调用来请求操作系统提供服务,而操作系统可以通过系统调用来向用户态的程序返回结果。

系统调用通常通过中断来实现。当用户态的程序触发一个系统调用时,CPU会发生特权级切换,从用户态切换到内核态。然后,操作系统会执行相应的服务,并通过系统调用返回结果。

如何通过系统调用请求服务

用户态的程序可以通过两种方式来通过系统调用请求服务:

  • 直接调用系统调用指令: 这种方式需要用户态的程序直接调用系统调用指令(通常是int 0x80)。这种方式比较底层,需要用户态的程序了解系统调用的具体实现细节。
  • 通过库函数调用系统调用: 这种方式是通过库函数来调用系统调用。库函数会负责生成系统调用指令,并调用系统调用指令。这种方式比较简单,不需要用户态的程序了解系统调用的具体实现细节。

结语

系统调用是用户态程序和操作系统之间交互的重要机制。通过系统调用,用户态程序可以请求操作系统提供服务,而操作系统可以通过系统调用向用户态程序返回结果。