如果不是用编程语言实现的话,“系统调用”是什么意思?


14

我想了解“系统调用”一词。我很熟悉,系统调用用于从用户空间应用程序获取内核服务。

我需要澄清的部分是“系统调用”和“系统调用的C实现”之间的区别。

这句话使我感到困惑:

在类似Unix的系统上,该API通常是C库(libc)的实现的一部分,例如glibc,该库为系统调用提供包装函数,通常将其命名为与调用的系统调用相同的名称。

什么是“他们调用的系统调用”?他们的来源在哪里?我可以直接将它们包含在我的代码中吗?

从一般意义上讲,“系统调用”只是一个POSIX定义的接口,但实际上要查看实现,可以检查C源代码,并在其中查看内核通信的实际用户空间实际上如何运行?

背景说明:最后,我试图了解每个c函数是否最终与中的设备进行交互/dev

Answers:


21

系统调用本身就是一个概念。它们代表进程可以要求内核执行的操作。

这些系统调用在类UNIX系统的内核中实现。该实现(用C语言编写,并用asm编写小部分内容)实际上在系统中执行操作。

然后,进程使用一个接口来请求系统执行系统调用。该接口由POSIX指定。这是C标准库的一组功能。它们实际上是包装器,它们可以执行一些检查,然后在内核中调用特定于系统的功能,以告知其执行系统调用所需的操作。诀窍在于,作为接口的那些函数的名称与系统调用本身相同,并且通常直接称为“系统调用”。

您可以在内核中调用直接通过特定于系统的机制执行系统调用的函数。问题在于它使您的代码绝对不可移植。

因此,系统调用为:

  • 一个概念,由内核执行以向用户进程提供服务的一系列动作
  • 您应该在代码中使用的C标准库的功能,以从内核获取此服务。

1
您有“包装函数”的示例和实际的系统调用吗?(Linux中的文件路径或到源的链接)
TheMeaningfulEngineer 2014年

3
例如,这是getpidLinux内核中系统调用的实现:lxr.free-electrons.com/source/kernel/timer.c?v=2.6.35#L1337。这是GNU C标准库glibc-2.19:fossies.org/dox/glibc-2.19/…中的包装函数。
lgeorget 2014年

@Igeorget:您的链接不再起作用。内核实现的更新链接:github.com/torvalds/linux/blob/…。这些天我找不到glibc的功能,该代码无法导航。
rchard2scout

6

一个系统调用是一种方法,让您的操作系统(内核)做代表你的程序的一些操作,该程序本身不能做的(或仅仅是不方便)。无法执行某些操作的原因通常是允许随机程序执行这些操作可能会损害系统的完整性,例如执行I / O(直接对RAM进行覆盖,覆盖任何内容)。

POSIX为程序定义了接口,程序可以调用某些功能。其中一些或多或少直接转换为系统调用,另一些则需要更多的阐述。它是您的语言的运行时,例如C库,负责提供POSIX接口,并打包参数并将结果返回给调用方。

Unixy系统或多或少直接提供POSIX接口作为系统调用。通常,有一种方法可以直接调用系统调用,以查找syscall(2)有关如何在Linux中使用此功能的详细信息。


1
您触及了其他答案刚刚提出的重要观点。任何功能相当能够程序员可以为自己写(例如strlenstrcpysqrt,和qsort)可以,可能是在用户空间,从库中加载。(大多数为libc;数学函数sqrt以及三角函数和双曲线函数可能位于libm的数学库中。)…(续)
Scott

1
(续)…但是用户无法编写自己的forkkillopen函数,因为它们需要访问操作系统内核内存空间(例如进程表)或特权指令(例如I / O)。因此,执行这些功能的代码必须在操作系统内核中。因此,系统功能或系统调用。
斯科特

5

当然,让我们来看看这个大象有多少方向?事情。

在您构建的程序中,实际的系统调用是触发特权升级为内核模式的机器指令,而在内核本身中,则是该指令所调用的代码。libc代码(以及每种语言运行时)在内核代码希望找到它们的地方设置机器寄存器和存储参数,由于该机器指令的限制,这些参数可以确定为奇数位。

一旦进入了OS代码本身,就会对userland运行时所做的特定于机器的东西进行一些镜像解散,然后进行完美的普通子例程调用。
如果您想确切了解它在完整操作系统中的工作原理,请提取内核源代码(git clone https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/)并执行eg git grep -i system\ call。拉出glibc源,然后执行同样的操作。


没错,但是在Linux或glibc中
翻腾

3

在Linux中,至少系统调用机制在大多数体系结构下都可以通过将一些特定格式的数据(通常是某种结构)放置在某些寄存器或预定义的内存地址中来工作。

但是,问题在于实际上迫使CPU将其切换到内核空间,以便它可以运行特权内核代码来为调用提供服务。这是通过强制某种故障(故障被0除,未定义的溢出或段错误等)来完成的,这迫使内核接管执行以处理故障。

通常,内核通过杀死引起原因的进程或运行用户提供的处理程序来处理错误。但是,在进行系统调用的情况下,它将改为检查预定义的寄存器和内存位置,如果它们包含系统调用请求,它将使用内存结构中用户进程提供的数据来运行该请求。这通常必须通过一些特殊的手工组装来完成,并且为了便于用户使用syscall,系统的C库必须将其包装为函数。对于较低级别的界面,请参阅http://man7.org/linux/man-pages/man2/syscall.2.html,以获取有关syscall如何工作以及如何在不使用C包装程序的情况下进行调用的一些信息。

这样做过于简单,并非在所有体系结构中都适用(mips具有特殊的syscall指令),并且不一定在所有OS上都可以正常工作。不过,如果您有任何意见或疑问,请询问。

修订:请注意,关于您对/ dev /中的内容的评论,这实际上是内核的更高级别的接口,而不是更低级别的接口。这些设备实际上使用下面的(大约)4个系统调用。向它们写入与写入syscall相同,读取已读取的syscall,相当于打开和关闭syscall来打开/关闭它们,然后运行ioctl会导致特殊的ioctl syscall,它本身就是访问系统多个ioctl之一的接口。调用(特殊的,通常是设备特定的调用,其使用范围太窄,无法为它们编写整个syscall)。


1

每个系统调用都有一个关联的整数。该整数是系统调用的返回值,系统调用的参数数量和参数类型的函数。该系统调用号不过是全局系统调用向量的偏移量,该向量只能在特权模式下访问,它包含指向适当处理程序的指针。调用系统调用的过程将生成软件中断(陷阱中断),因此将运行陷阱处理程序,该处理器确定应调用哪个系统调用。然后,内核会将堆栈上用户传递的系统调用的参数复制到处理器寄存器中,并在完成请求的服务后,将数据从处理器寄存器复制回堆栈中。这是系统调用的参数有限的原因之一,


每个调用(操作)在内部都由数字true标识。但是数量取决于操作,而不取决于返回值或参数数量。它是如何用来从用户态的x86工作的解释是这里
vonbrand
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.