Linux从头学13:想彻底搞懂系统调用的底层原理?建议您别错过这篇调用门
2023-10-15 12:02:31
导语
在之前的文章《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会发生特权级切换,从用户态切换到内核态。特权级切换的过程如下:
- CPU将当前的段寄存器值压入堆栈。
- CPU将调用门的段选择符装入段寄存器。
- CPU将调用门的偏移量装入指令指针寄存器。
- CPU执行中断处理程序中的代码。
内核态和用户态之间的交互
内核态和用户态之间的交互主要通过系统调用来实现。用户态的程序可以通过系统调用来请求操作系统提供服务,而操作系统可以通过系统调用来向用户态的程序返回结果。
系统调用通常通过中断来实现。当用户态的程序触发一个系统调用时,CPU会发生特权级切换,从用户态切换到内核态。然后,操作系统会执行相应的服务,并通过系统调用返回结果。
如何通过系统调用请求服务
用户态的程序可以通过两种方式来通过系统调用请求服务:
- 直接调用系统调用指令: 这种方式需要用户态的程序直接调用系统调用指令(通常是int 0x80)。这种方式比较底层,需要用户态的程序了解系统调用的具体实现细节。
- 通过库函数调用系统调用: 这种方式是通过库函数来调用系统调用。库函数会负责生成系统调用指令,并调用系统调用指令。这种方式比较简单,不需要用户态的程序了解系统调用的具体实现细节。
结语
系统调用是用户态程序和操作系统之间交互的重要机制。通过系统调用,用户态程序可以请求操作系统提供服务,而操作系统可以通过系统调用向用户态程序返回结果。