系统调用有哪些类别?如何进行?有什么作用?
【引言】
有朋友要求学习一下系统调用的知识。我们就通过这篇文章来了解一下。
【概述】
在计算机中,系统调用(通常缩写为syscall)是计算机程序从操作系统的内核请求服务的程序化方式。这可能包括与硬件相关的服务(例如,访问硬盘驱动器),创建和执行新进程,以及与内核服务(如进程调度)的通信。系统调用提供了进程和操作系统之间的重要接口。
在大多数系统中,系统调用只能由用户空间的进程来进行,而在某些系统中,例如在OS/360及其继任者中,有特权的系统代码也会发出系统调用。
【系统调用的类别】
系统调用大致可以分为六大类:
1. 过程控制
a. 创建进程
b. 终止进程
c. 载入、执行
d. 获取/设置过程属性
e. 等待时间、等待事件、信号事件
f. 分配和释放内存
2. 文件管理
a. 建文件、删文件
b. 打开文件、关闭文件
c. 读、写、调位置
d. 获取/设置文件属性
3. 设备管理
a. 请求设器、释放设器
b. 读、写、调位位置
c. 获取/设置设备属性
d. 连接或断开设备
4. 信息维护
a. 获取/设定时间或日期
b. 获取/设置系统数据
c. 获取/设置进程、文件或设备属性
5. 通信
a. 建立、断开通信
b. 收发信息
c. 转移状态信息
d. 连接和断开远程设备
6. 保护措施
a. 获取/设置权限
【安全模型】
除了一些嵌入式系统外,大多数现代处理器的架构都涉及到安全模型。例如,环形模型指定了软件可以执行的多个权限级别:一个程序通常被限制在自己的地址空间内,这样它就不能访问或修改其他运行中的程序或操作系统本身,并且通常被阻止直接操作硬件设备(如帧缓冲区或网络设备)。
但是,很多时候许多应用程序需要访问这些组件来完成自己的任务,因此操作系统就提供了系统调用,为这类操作提供定义良好的、实现安全的通信方式。
操作系统以最高级别的权限执行,允许应用程序通过系统调用请求使用服务,而系统调用通常是通过中断发起的。中断会自动使CPU进入某种高权限级别,然后将控制权传递给内核,由内核决定调用程序是否应该被授予所请求的服务。
如果请求服务被允许的话,内核会执行一组特定的指令来完成任务。而调用程序对内核没有直接控制权,任务完成后将控制权返回给调用程序。
【程序库API】
一般来说,系统环境会提供了一个程序库来暴露一些API,这些API可以在应用程序和操作系统之间完成通信任务。
在类似Unix的系统中,这些API通常是C程序库库(libc)实现的一部分,例如glibc,它为系统调用提供了封装函数,通常与系统调用的名称相同。
在Windows NT上,这个API是Native API的一部分,在ntdll.dll库中;这是一个未公开发行的API,被常规Windows API的实现所使用,也被Windows上的一些系统程序直接使用。该库的封装函数层提供了一个普通函数调用的方法(是汇编级的子程序调用),用于使用系统调用,这也使得系统调用更加模块化。
在这里,封装层的主要功能是将所有要传递给系统调用的参数都放在相应的处理器寄存器中(有时也可以放在调用栈中),同时也为内核设置一个唯一的系统调用编号来调用。这样一来,存在于操作系统和应用程序之间的库就增加了可移植性。
对库函数本身的调用不会导致切换到内核模式,通常是正常的子程序调用(例如,在某些指令集架构(ISA)中使用 "CALL"汇编指令)。
实际的系统调用确实将控制权转移到了内核(而且这比抽象的库调用更依赖于具体的实现和平台环境)。例如,在类似Unix的系统中,fork和execve是C库函数,这些函数反过来调用fork和exec这些系统调用的指令。
直接在应用程序代码中进行系统调用比较复杂,可能需要使用嵌入式汇编代码(在C和C++中),并且需要了解系统调用操作的底层二进制接口,而这些二进制接口可能会随着时间的推移而变化,它们不是应用程序二进制接口的一部分,而程序库函数的作用就是为了抽象出这些逻辑而创建的。
在基于exokernel的系统中,库作为中介的作用尤为重要。在exokernels上,库将用户应用屏蔽在非常低级的内核API中,并提供抽象层和资源管理。
由OS/360和DOS/360衍生出来的IBM操作系统,包括z/OS和z/VSE,都是通过汇编语言宏程序库来实现系统调用的。它们的起源于汇编语言编程比高级语言更普遍的年代。因此,IBM的系统调用不能被高级语言程序直接调用,而是需要一个封装的可调用的汇编语言子程序。
【系统中的示例和检测工具】
在Unix、Unix-like和其他兼容POSIX的操作系统上,常用的系统调用有open、read、write、close、wait、exec、exc、fork、exit和kill。许多现代操作系统都有数百个系统调用。例如,Linux和OpenBSD各有300多个不同的调用,NetBSD有近500个,FreeBSD有500多个,Windows 7有近700个,而Plan9有51个。
有些工具如strace、ftrace和truss等可以从进程一开始就跟踪报告该进程调用的所有系统调用,或者可以把这些工具绑定附加到一个已经运行的进程上来跟踪其进程调用情况。只要该追踪操作不违反用户的权限,就可以拦截该进程所做的任何系统调用。这些程序工具的这种特殊能力通常也是通过系统调用来实现的,例如strace是通过ptrace或procfs中的文件的系统调用来实现。
【典型的实现方式】
实现系统调用需要将控制权从用户空间转移到内核空间,这涉及到某种特定的架构特性。一个典型的实现方式是使用软件中断。中断将控制权从用户程序转移到操作系统内核,因此软件只需要设置一些带有系统调用编号的寄存器,通过触发这些寄存器内容的变化来执行软件中断。
在许多RISC处理器上软中断是系统调用的唯一可用技术,但CISC架构如x86等还支持其他技术。
例如,x86指令集包含了SYSCALL/SYSRET和SYSENTER/SYSEXIT指令(这两种机制分别由AMD和Intel独立创建,但实质上它们的作用是一样的)。
这些指令集都是为了"快速"控制转移指令,其目的是为了在没有中断发生的情况下,将系统调用的控制权快速转移到内核中。
Linux 2.5版本在x86上开始使用这项技术;以前它使用的是中断(INT)指令,那时的方式是先将系统调用编号0x80写入在EAX寄存器中,再触发中断。
一种较老的系统调用机制是调用门。最初它在Multics中使用,后来在Intel x86上使用。
它允许程序直接使用操作系统事先设置的安全控制转移机制来调用内核函数。这种方法在x86上一直不受欢迎,大概是由于使用x86内存分段的远程调用的需求以及由此导致的可移植性不够。而且x86上有上面所说的更快的方式。
对于IA-64架构,采用了EPC(EnterPrivileged Code)指令。前8个系统调用参数在寄存器中传递,其余的在栈中传递。
在大多数IBM自己的操作系统中,比如IBM的System/360大型机系列及其后继者中,在Linux的所有系统调用指令中,都实现了对遗留设施的系统调用。
而值得注意的是,在IBM的System/360大型机系列及其后继者中,存在一个观察者调用(SupervisorCall)指令,其编号是在指令中而不是在寄存器中,这实现了对遗留设施的系统调用。
在IBM自己的操作系统中,程序调用(PC)指令用于较新的设施。特别地,当调用者处于SRB模式时,程序调用(PC)会被使用
【处理器模式和上下文切换】
在大多数类似Unix的系统中,系统调用都是在内核模式下进行处理的,而内核模式是通过将处理器的执行模式改变为更多的特权模式来实现的,但不需要进行进程上下文切换--尽管特权上下文切换确实会发生。硬件根据处理器状态寄存器的执行模式来看待周边情况,而进程是操作系统提供的一个抽象概念。一个系统调用一般不需要对另一个进程进行上下文切换;相反,无论哪个进程调用了它,它都会在那个进程的上下文中进行处理。
在多线程进程中,系统调用可以由多个线程进行。对这类调用的处理取决于具体的操作系统内核和应用程序运行时环境的设计。下面列出了操作系统所遵循的典型模式:
多对一模式。进程中任何用户线程的所有系统调用都由一个内核级线程处理。这种模式有一个严重的缺点--任何阻塞的系统调用(如等待用户的输入)都会冻结所有其他线程。另外,由于一次只能有一个线程访问内核,这种模式无法利用多个内核的处理器。
一对一模型。每个用户线程在系统调用过程中,都会被连接到一个独立的内核级线程上。这种模式解决了上述阻断系统调用的问题。它在所有主流的Linux发行版、macOS、iOS、最近的Windows和Solaris版本中都得到了应用。
多对多模式。在这个模型中,一个用户线程池被映射到内核线程池中。来自用户线程池的所有系统调用都由相应的内核线程池中的线程处理。
混合模型。这种模式结合了多对多和一对一模式,最终取决于内核的选择。这在旧版本的IRIX、HP-UX和Solaris中都比较常见。
【小结】
本文对于系统调用做了探讨和学习,一般来说,所有需要资源的程序都必须通过系统调用才能完成任务,希望对大家有所裨益。
欢迎讨论。
【参考资源】
https://www.geeksforgeeks.org/introduction-of-system-call/
http://fxr.watson.org/fxr/source/kern/syscalls.master
https://en.wikipedia.org/wiki/System_call
https://www.guru99.com/system-call-operating-system.html
http://man7.org/linux/man-pages/man2/syscalls.2.html
- 点赞
- 收藏
- 关注作者
评论(0)