2.4 系统调用
-
系统调用,就是用户在程序中调用操作系统所提供的一些子功能。
-
系统调用是操作系统为了增强系统功能,方便用户使用而建立的,是操作系统提供给编程使用的唯一接口。
2.4.1 系统调用的概念和类型
(一)系统调用的概念
系统调用是操作系统提供给用户程序的一种接口,它允许用户程序请求操作系统的服务。当用户程序需要操作系统的功能,如文件操作、进程控制、通信等,它会通过系统调用来实现。系统调用是用户态程序与内核态程序之间的桥梁。
(二)系统调用与一般过程调用的区别
-
执行环境:
- 系统调用:当用户程序执行系统调用时,它会从用户态切换到核心态,由操作系统内核来执行。
- 一般过程调用:过程调用发生在用户程序内部,执行时保持在同一状态(用户态),不涉及状态切换。
-
权限:
- 系统调用:需要操作系统的权限来执行,因为它们通常涉及对系统资源的访问和控制。
- 一般过程调用:不需要特殊权限,因为它们只涉及程序内部的函数调用。
-
目的:
- 系统调用:用于请求操作系统提供的服务,如文件操作、进程控制等。
- 一般过程调用:用于执行程序内部定义的函数,如计算、数据处理等。
-
中断机制:
- 系统调用:通常通过软件中断来实现,触发操作系统的中断处理程序。
- 一般过程调用:通过正常的函数调用机制实现,不涉及中断。
(三)系统调用的分类
系统调用可以根据它们提供的功能和服务类型进行分类。以下是一些常见的分类方式:
-
文件操作类:
open
:打开文件。read
:读取文件内容。write
:写入文件内容。close
:关闭文件。
-
进程控制类:
fork
:创建子进程。exec
:执行新程序。wait
:等待进程结束。
-
通信类:
pipe
:创建管道。socket
:创建套接字。send
和recv
:发送和接收消息。
-
设备操作类:
ioctl
:控制设备。read
和write
:对设备进行读写操作。
-
信息维护类:
getpid
:获取进程ID。gettimeofday
:获取系统时间。
-
安全和保护类:
chmod
:改变文件权限。chown
:改变文件所有者。
-
内存管理类:
sbrk
或mmap
:动态内存分配。munmap
:释放内存。
-
信号处理类:
kill
:发送信号。signal
:设置信号处理函数。
系统调用的具体实现和分类可能因操作系统的不同而有所差异,但上述分类提供了一个通用的框架来理解系统调用的类型和用途。
2.4.2 系统调用的实现
系统调用的实现涉及到用户态和核心态之间的切换,以及操作系统内核对请求的处理。以下是系统调用实现的基本步骤:
-
用户程序发起请求:
用户程序通过执行一个特殊的系统调用指令(如int
指令在x86架构中)来发起系统调用请求。这通常涉及到设置一个系统调用号和必要的参数。 -
中断和上下文切换:
特殊的系统调用指令会触发一个中断,导致CPU从用户态切换到核心态。操作系统会保存当前用户程序的状态(上下文),以便之后可以恢复执行。 -
参数传递:
用户程序在发起系统调用时会传递参数,这些参数在内核中被解析,以便内核知道要执行的具体服务。 -
内核处理请求:
操作系统内核会根据系统调用号和传递的参数执行相应的服务。这可能涉及到访问文件系统、管理进程、处理I/O请求等。 -
执行系统调用:
内核中有一个系统调用表,它将系统调用号映射到具体的内核函数。内核会查找这个表,执行对应的函数来处理请求。 -
返回结果:
系统调用完成后,内核会将结果(如果有的话)返回给用户程序。如果系统调用成功,它会返回一个非负值;如果失败,它会返回一个错误码。 -
上下文恢复和返回用户态:
内核处理完请求后,会恢复用户程序的执行状态(上下文),并将CPU控制权交还给用户程序。 -
用户程序继续执行:
用户程序从系统调用指令的下一条指令继续执行,这时它已经得到了操作系统服务的结果。
(四)系统调用的实现机制
-
中断描述符表(IDT):在x86架构中,系统调用通过中断机制实现。用户程序执行的系统调用指令会触发一个软件中断,这个中断的处理器会在中断描述符表中查找对应的处理程序。
-
系统调用号:每个系统调用都有一个唯一的系统调用号,这个号码用于在系统调用表中查找对应的内核函数。
-
寄存器:系统调用的参数通常通过CPU寄存器传递给内核,例如在x86-64架构中,前六个整数或指针参数通常通过
rdi
、rsi
、rdx
、r10
、r8
和r9
寄存器传递。 -
内核栈:内核通常会使用专门的内核栈来处理系统调用,这与用户程序的栈是分开的。
-
错误处理:如果系统调用失败,内核会设置一个错误码,并通过特定的寄存器(如
rax
在x86-64架构中)返回给用户程序。
系统调用的实现细节在不同的操作系统和硬件架构中可能会有所不同,但上述步骤提供了一个通用的概览。