【问题标题】:could anyone explain to me about system calls in programming languages?谁能向我解释一下编程语言中的系统调用?
【发布时间】:2013-07-26 07:04:54
【问题描述】:

几天前我开始研究操作系统概念,但我已经遇到了一些问题。 主要是我对系统调用很好奇。 我了解到每个操作系统都提供了自己的 API (如Windows for Windows API,Linux for libc 等)

我开始感到困惑的是包装函数。 例如 Linux 有一个 fork() 包装函数。 这是否意味着此函数内部的算法执行系统调用例程 基于操作系统的系统调用表? 而且我不明白它是用 C 语言编写的。 这是否意味着它正在使用 C 标准库?还是只是 C 编译器? 此外,即使 C 编译器如何编译它的标准库 有不同种类的编译器,如 GCC windows C 编译器等? 我很好奇的是,C 标准库函数也调用系统调用对吗? 但它可能必须调用不同的系统调用才能在不同的操作系统中获得相同的输出,对吗? 这是否意味着,即使它是相同的标准 C 函数,系统调用 内部实现因操作系统而异?

我真的想要一个系统调用的好概念 plz :)

【问题讨论】:

    标签: operating-system


    【解决方案1】:

    系统调用是底层操作系统公开的功能。一种语言通常有一种明确定义的方式来发出系统调用。例如,Intel x86 处理器使用了syscall 指令,但您也可以使用其他方式,例如发出中断指令。

    进行系统调用时,会发生从用户空间到内核空间的转换,内核空间在更高的特权级别上运行。系统调用的参数被复制到内核空间并进行验证,以确保它们不会危及内核。但是,这些参数很可能也在用户空间中得到验证。在某些情况下,用户空间函数将是内核函数的瘦包装器,而在其他情况下,它们会在顶部提供附加功能。

    C 和 Unix 有着紧密的传统,这就是为什么很多系统调用名称与它们的 C 版本相同(例如 fork、execXXX)。但是,这只是一个约定,并非所有地方都如此。在 Windows 上,系统调用的名称与其 Linux 对应的名称完全不同。但是,平台上的底层 C 运行时实现对您隐藏了这一点,并进行正确的系统调用以实现 C 函数提供的功能。

    像 Ruby 这样的高级语言通常不会直接发出系统调用。相反,他们使用以较低级别语言(例如 C 或 C++)编写的库,将 Ruby 调用转换为对底层系统函数的调用。

    【讨论】:

      【解决方案2】:

      我对Linux内核知之甚少,所以我将在回答中以Linux内核为例。要了解系统调用,您必须了解内核空间和用户空间的概念。系统调用是用户空间应用程序与内核通信的接口。所以系统调用的实现是内核的一部分。

      首先,Linux 内核主要使用 C 语言编写,部分使用汇编语言编写。但是 Linux 内核从不使用任何 C 标准库函数。所以在Linux中,不可能用C标准库函数来实现系统调用。

      其次,Linux 提供了数百个系统调用。其中一些是用 C 编写的,但其中一些必须用汇编语言实现。不仅用 C 编写的程序可以进行系统调用,大多数其他语言如 Ruby、Golang 也可以做到这一点。恕我直言,说系统调用是用 C 编写的是不正确的。

      第三,不同的操作系统提供不同的系统调用集。即使是同一个操作系统也可以在不同的架构上运行,例如 x86、arm 等。不同的架构意味着不同的指令集。因此,即使是相同的操作系统也可以以不同的方式实现相同的系统调用。所以我认为您对此的理解是正确的。

      【讨论】:

        【解决方案3】:

        系统调用和包装函数是两个不同的概念。

        1. 系统调用: 这些是内核空间中的普通函数。它们不能被用户程序直接访问,即像你我这样的人编写的函数。系统调用只能由内核代码访问。这些函数将与设备或主内存交互,为用户程序提供请求的服务。

        2. 包装函数: 当你想在你的用户代码中使用系统调用时,你不能像在用户空间中使用函数那样直接调用它。您的处理器需要执行陷阱机器指令(中断)才能切换到内核模式。一旦切换到内核模式来执行系统函数,传递给该函数的参数中应该没有错误。系统调用不会检查其参数的正确性,因此如果参数有任何问题,可能会导致内核错误。如果您的用户空间程序出现故障,内核只会杀死您的进程,但是当内核出现故障时,您的整个内核都会崩溃,这是不可接受的。 包装函数确保所有参数都是有效的。如果一切正常,他们还会设置基础以捕获内核。

        现在回答你的问题:

        1. 包装函数不执行系统调用函数,它们只检查参数的正确性并通过传递适当的系统调用号将陷阱设置为内核模式。您的内核包含一个按编号排列的系统调用表并执行请求的函数。一旦陷阱被包装函数调用,它的工作就结束了。

        2. 包装函数是常规的用户级程序,用 C 语言编写,部分用汇编语言编写。是的,包装函数可以调用标准库函数。

        3. 标准库函数是用 C89、C99 版本和旧版本的 C 编写的。这无关紧要,因为新版本的 C 向后兼容旧版本。所以是的,库函数可以像普通用户代码一样编译,任何版本的 C 编译器都不会出现问题。

        4. 大多数标准库函数在多个操作系统和编译器中都是相同的,但是是的,也存在一些差异。 fork() 包装函数在 Visual Studio 中不起作用,因为 Windows 有不同的方法。

        编辑:fork() 不是系统调用,它只是一个内部陷阱到内核的库函数

        【讨论】:

        • 您确定系统调用不检查它们的参数吗?对我来说,这对于现代操作系统来说似乎非常不安全。
        • 当您将用户空间指针传递给系统调用时,它会为传递的虚拟地址创建新的虚拟地址,但是这两个虚拟地址都指向相同的物理地址。系统调用不检查传递参数的有效性或正确性。这些功能由包装函数处理。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-10-10
        • 1970-01-01
        • 1970-01-01
        • 2017-04-07
        相关资源
        最近更新 更多