【问题标题】:Why do system calls exist为什么存在系统调用
【发布时间】:2018-11-10 14:15:54
【问题描述】:

我一直在阅读有关系统调用以及它们如何在 Linux 中工作的信息。我还有更多的阅读要做,但我没有回答的一件事是,为什么我们需要系统调用?

我知道系统调用是用户空间程序要求内核做某事的请求,但我的问题基本上是:为什么用户空间程序不能自己做这件事?为什么 Glibc 不做实际操作而只是作为系统调用的包装器?

例如,如果我在我的程序中调用fopen(),为什么glibc 调用open 系统调用?为什么 glibc 不自己做操作?

我知道这意味着 glibc 开发人员将有更多的工作,他们必须对 Linux 有深入的了解,但 glibc 不是已经与 Linux 内核密切相关吗?

另外,我知道系统调用函数是在 CPU 的 ring 0 中运行的……但这样做的真正意义是什么?如果我执行一个程序,我就授予它运行的明确权限,那么通过分离哪些代码可以在不同的上下文中运行来增加什么安全性,因为无论如何你都授予它所有权限?

【问题讨论】:

  • 为什么用户空间程序不能自己做事情 - 它可以。在 DOS 中。然后人们意识到,为了更大的利益,他们需要保护某些东西不被任何人访问。
  • 因为操作系统(即 linux 内核)必须管理硬件资源并协调其他用户程序也发出请求 - 所以它必须具有该权限。如果个别程序只是做他们喜欢做的事,你就会回到 DOS 的黑暗时代
  • 回想一下,主要工作之一是为系统提供安全性和稳定性。如果任何旧的用户空间程序能够为所欲为,它就无法做到这一点。因此,用户空间到内核的接口是存在的,所以内核可以确定程序只能做它允许做的事情。
  • "为什么 Glibc 不做实际操作,而只是作为系统调用的包装器?"你为什么不把这个推得更远一点?为什么程序员不自己做操作而不是使用?
  • 阅读Operating Systems: Three Easy Pieces,因为回答您的问题需要整本书

标签: c linux-kernel system system-calls glibc


【解决方案1】:

您的问题的完整答案非常广泛,但让我根据您关于 fopen 的问题举一个简单的例子。

假设我们有一个拥有成百上千用户的大型系统。人力资源部门说,其中一位用户拥有包含员工机密信息的文件。

如果可以在用户模式下随意访问该磁盘,那么系统上的任何人都可以打开系统上的任何文件,包括那些具有机密信息的文件。

换句话说,操作系统管理共享资源。其中包括磁盘、CPU 和内存。如果这些可以在用户模式下进行控制,就无法确保公平共享。

【讨论】:

    【解决方案2】:

    1- 您不想处理低级硬件通信。至少大多数人不会。它们每个都有数百个命令。

    2- 犯一个简单的错误,您的 CPU/RAM 或 I/O 设备可能永远无用。

    3- 当您是网络的一部分时,您可以共享资源。系统调用和内核可防止您的同事损坏您的硬盘。

    【讨论】:

      【解决方案3】:

      如果由 C 实现生成的代码是唯一将在目标系统上运行的东西(对于许多独立实现和极少数托管实现来说都是如此),并且如果实现知道 确切地说它将在什么硬件上运行(对于一些独立的实现是正确的,但对于托管的实现很少如此),它的运行时库可能能够通过直接与存储硬件通信来执行诸如“fopen”之类的操作。然而,这两种情况都适用的情况很少见,更不用说同时适用了。

      如果多个程序将使用存储设备,通常需要它们以某种方式协调它们的操作,否则不同程序执行的操作序列不会重叠,并且每个程序都会“忘记”它认为自己知道的任何事情任何时候其他程序可能写入的存储状态。

      否则,假设一个磁盘包含一个文件并且程序 #1 使用“fopen”打开它以供读取。每个目录扇区包含 8 个条目,因此程序将读取第一个目录扇区并观察插槽 #0 标识感兴趣的文件,而 #1-#7 为空白。

      现在假设程序#2 使用“fopen”来创建一个用于写入的文件。它会读取目录扇区,观察插槽#1-#7 是空白的,然后用关于插槽#1 中的新文件的信息重写目录扇区。

      最后,假设程序#1 想要写一个文件。如果它不知道程序#2,它可能有理由相信它知道目录包含的内容(它之前已经阅读过它,并且没有理由相信它已被更改),将有关新文件的信息放在插槽#1 中,并且用新版本替换磁盘上的目录扇区,删除程序 #2 写入的条目。

      让两个程序通过操作系统路由它们的操作确保当程序 #2 想要创建它的文件时,它可以利用它刚刚读取程序 #1 的目录这一​​事实(因此不需要重新读取它)。更重要的是,当程序#1 去写一个文件时,操作系统会知道该目录包含程序#2 写入的文件,从而确保新文件被放置在#2 槽中。

      与其他答案所说的相反,即使是在 MS-DOS 等平台上运行的微型计算机 C 实现也基本上总是依赖操作系统进行文件 I/O。有些会包括他们自己的控制台 I/O 例程,因为 MS-DOS 中的那些例程大约是它们应有的速度的四倍,但是在使用文件 I/O 时需要协调意味着很少有程序会尝试这样做自己。

      【讨论】:

      • 我曾经用一个小程序覆盖软盘的引导记录来清除隐藏在其中的病毒(80年代后期...)。好的,它不是用于标准文件处理,但碎片整理工具可以做到这一点......
      【解决方案4】:

      当先前空闲的磁盘旋转两秒,或者网络磁盘连接三十秒时,库要做什么?

      【讨论】:

      • 也许它可以思考为什么使用system 会产生同样的延迟?这没有回答问题。
      【解决方案5】:

      为什么 glibc 不自己做操作?

      好吧,在好的旧 MS/DOS 系统中,事情的发展方式要少得多:内核代码和用户代码之间没有分离,用户代码可以愉快地直接访问硬件。

      这只有两个主要问题:

      • 它在单个用户而不是多任务系统上工作(相当)良好,但只要多个程序可以在系统中同时运行,您必须同步硬件访问和内存使用 => 这些是专用于内核
      • 没有保护系统免受编码不良的程序的影响。在现代操作系统中,错误的程序可能会崩溃,但系统本身应该能够存活。在 MS/DOS 中,程序崩溃通常以系统重启结束。

      出于这些原因,所有现代操作系统(可能除了一些轻量级的嵌入式操作系统)都在不同的用户进程和内核之间使用隔离。这只是意味着您需要一种方法来允许用户模式进程要求内核执行特权操作(读取或写入物理磁盘is):这正是系统调用的目的。

      【讨论】:

        【解决方案6】:

        另一个考虑因素是操作系统内核需要通过统一的 API 为无数不同类型的硬件提供抽象——没有它,您总是会在程序中进行特定于设备的调用。

        【讨论】:

        • 不是一个真正的原因。提供内核模式驱动程序或用户模式库没有太大区别,两者都可以提供通用接口。
        【解决方案7】:

        为什么 glibc 不自己做操作?

        简短回答:因为它不能。


        长答案: 在 Linux 中运行的程序可以在两种模式下运行:UserLand 或 KernelLand。

        Kernel Land 拥有一切权利,可以做任何事情,包括与硬件通信或提供用户空间回调。例如,当您调用fopen() 时,内核会与您的文件系统(例如ext4)进行所有肮脏的对话、缓存以及与 SATA 控制器对话以访问硬盘驱动器上的数据。

        GLibc 可以使用内核在 /dev 中公开的设备来做到这一点,但这意味着从头开始重新编码所有文件系统层、套接字、防火墙...

        内核只是为程序员提供了易于使用的 API 以提高权限并与设备进行通信。 这就是 Linux(以及大多数现代操作系统)的制作方式。

        既然您已经授予了所有权限,那么通过分离可以在不同上下文中运行的代码来增加什么安全性?

        权限由内核管理。如果您没有系统调用,则您没有权限。还是您运行的程序应该检查自己的权限?再一次,它每次都在重新发明轮子。

        【讨论】:

        • 我不会说一个程序(实际上是一个进程)在内核模式下运行。它只运行在用户态,内核运行在内核态并向程序提供服务(通过系统调用)
        • “内核”实际上并不作为一个进程存在。一个程序(实际上是一个线程)在使用系统调用时确实在内核模式下运行。当系统调用结束时,它以用户模式返回。
        • 我知道。但我不认为程序在内核模式下运行。程序通过系统调用向内核请求服务。但随后,该进程不再运行
        • 取决于你如何告诉它和操作系统。在 Linux 中,没有进程上下文切换,因此它是完全相同的进程,具有相同的内存和运行内核协程的 PID。
        • 我认为@BasileStarynkevitch 有一个很好的观点,即使我们有这个故事说内核不是一个进程。说程序处于内核模式可能意味着它有能力在内核模式下执行自己的代码。然而,它是一个已经定义的代码(内核代码),它正在执行,所以它就像程序要求内核为它做事。但这并不强迫我们说内核是一个进程。
        猜你喜欢
        • 2014-09-28
        • 2015-10-05
        • 2017-03-20
        • 2019-01-02
        • 2017-09-12
        • 1970-01-01
        • 2014-07-07
        • 1970-01-01
        • 2017-06-24
        相关资源
        最近更新 更多