GRA*_*ZER 55 operating-systems kernel
我知道当源代码(比如 C++)被编译时,编译器的输出是机器代码(可执行),我认为它是直接发送给 CPU 的指令。最近我正在阅读内核,我发现程序不能直接访问硬件,而必须通过内核。
所以当我们编译一些简单的源代码,比如一个printf()函数,并且编译产生可执行的机器码时,这个机器码中的每条指令是直接从内存中执行(一旦代码被操作系统加载到内存中)还是会机器码中的每一条命令还需要经过OS(内核)才能执行?
我读过一个类似的问题。没有说明编译后生成的机器码是直接发给CPU的指令,还是需要再次通过内核为CPU创建正确的指令。即,机器代码加载到内存后会发生什么?它会通过内核还是直接与处理器对话?
saw*_*ust 89
作为编写无需操作系统即可执行的程序的人,我提供了明确的答案。
可执行文件需要操作系统内核才能运行吗?
这取决于该程序的编写和构建方式。
您可以编写一个根本不需要操作系统的程序(假设您有知识)。
这样的程序被描述为独立的。
引导加载程序和诊断程序是独立程序的典型用途。
然而,在某些主机操作系统环境中编写和构建的典型程序将默认在同一主机操作系统环境中执行。
编写和构建独立程序需要非常明确的决策和行动。
...编译器的输出是机器代码(可执行文件),我认为它是直接发送给 CPU 的指令。
正确的。
最近我正在阅读内核,我发现程序不能直接访问硬件,而必须通过内核。
这是操作系统用于执行程序的 CPU 模式所施加的限制,并由某些构建工具(例如编译器和库)提供便利。
它并不是对曾经编写的每个程序的内在限制。
所以当我们编译一些简单的源代码时,比如只用一个 printf() 函数,并且编译产生可执行的机器码,这个机器码中的每条指令都会直接从内存中执行吗(一旦代码被操作系统加载到内存中) 还是机器代码中的每个命令仍然需要通过操作系统(内核)才能执行?
每条指令都由CPU执行。
不受支持或非法的指令(例如,进程没有足够的特权)将导致立即异常,而 CPU 将改为执行例程来处理这种异常情况。
甲printf()的功能不应该被用作示例“简单的源代码”。
从面向对象的高级编程语言到机器代码的转换可能并不像您暗示的那么简单。
然后从执行数据转换和I/O的运行时库中选择最复杂的函数之一。
请注意,您的问题规定了具有操作系统(和运行时库)的环境。
一旦系统启动,操作系统被赋予对计算机的控制权,程序的功能就会受到限制(例如 I/O 必须由操作系统执行)。
如果您希望执行独立程序(即没有操作系统),那么您一定不要启动计算机来运行操作系统。
...机器码加载到内存后会发生什么?
那要看环境了。
对于一个独立的程序,它是可以执行的,即通过跳转到程序的起始地址来移交控制权。
对于操作系统加载的程序,该程序必须与它所依赖的共享库动态链接。操作系统必须为将执行程序的进程创建一个执行空间。
它会通过内核还是直接与处理器对话?
机器代码由 CPU执行。
它们不“通过内核”,也不“与处理器交谈”。
机器码(由操作码和操作数组成)是对 CPU 进行解码并执行操作的指令。
也许您应该研究的下一个主题是CPU 模式。
Mok*_*bai 40
内核“只是”更多的代码。只是该代码是位于系统最低部分和实际硬件之间的一层。
所有这些都直接在 CPU 上运行,您只需向上过渡到它的各个层即可执行任何操作。
您的程序“需要”内核的方式与它需要标准 C 库的方式相同,以便首先使用该printf命令。
程序的实际代码在 CPU 上运行,但代码用于在屏幕上打印某些内容的分支通过 Cprintf函数的代码,通过各种其他系统和解释器,每个系统都进行自己的处理以计算出如何 hello world!实际上会打印在您的屏幕上。
假设您有一个在桌面窗口管理器上运行的终端程序,在您的内核上运行,而内核又在您的硬件上运行。
还有很多事情要做,但让我们保持简单......
hello world!hello world!写入控制台hello world!我写的,你可以把它在位置x,y好吗?”这是仅用于描述的大量过度简化。这里是龙。
实际上,您所做的所有需要硬件访问的事情,无论是显示、内存块、文件位还是其他任何东西,都必须通过内核中的某些设备驱动程序才能准确地确定如何与相关设备通信。它是位于 SATA 硬盘控制器驱动程序之上的文件系统驱动程序,该驱动程序本身位于 PCIe 桥接设备之上。
内核知道如何将所有这些设备连接在一起,并为程序提供一个相对简单的界面来做事情,而不必知道如何自己做所有这些事情。
桌面窗口管理器提供了一个层,这意味着程序不必知道如何绘制窗口并与其他试图同时显示内容的程序一起玩。
最后,终端程序意味着您的程序不需要知道如何绘制窗口,也不需要知道如何与内核显卡驱动程序对话,也不需要处理屏幕缓冲区和显示时间以及实际摆动窗口的所有复杂性。数据线到显示器。
这一切都由层层代码处理。
Jam*_*han 21
这取决于环境。在许多较旧(更简单!)的计算机中,例如 IBM 1401,答案是否定的。您的编译器和链接器发出了一个独立的“二进制文件”,它完全没有运行任何操作系统。当您的程序停止运行时,您加载了另一个程序,该程序也在没有操作系统的情况下运行。
在现代环境中需要操作系统,因为您一次不只运行一个程序。在多个程序之间同时共享 CPU 内核、RAM、大容量存储设备、键盘、鼠标和显示器需要协调。操作系统提供了这一点。因此,在现代环境中,您的程序不能只读取和写入磁盘或 SSD,它必须要求操作系统代表它执行此操作。操作系统从所有想要访问存储设备的程序中获取这样的请求,实现诸如访问控制(不允许普通用户写入操作系统的文件)之类的事情,将它们排队到设备中,并对返回的信息进行整理到正确的程序(进程)。
此外,现代计算机(与 1401 不同)支持连接非常广泛的 I/O 设备,而不仅仅是 IBM 在过去出售给您的设备。您的编译器和链接器不可能知道所有的可能性。例如,您的键盘可能通过 PS/2 或 USB 连接。操作系统允许您安装特定于设备的“设备驱动程序”,这些驱动程序知道如何与这些设备通信,但为操作系统提供设备类的通用接口。因此,您的程序,甚至操作系统,对于从 USB 与 PS/2 键盘获取击键,或访问本地 SATA 磁盘与 USB 存储设备与位于某处的存储,都无需做任何不同的事情在 NAS 或 SAN 上。这些细节由各种设备控制器的设备驱动程序处理。
对于大容量存储设备,操作系统在所有这些设备之上提供了一个文件系统驱动程序,该驱动程序为目录和文件提供相同的接口,而不管存储在何处以及如何实现。再说一次,操作系统担心访问控制和序列化。例如,通常情况下,不应该在不跳过某些环节的情况下一次打开同一个文件以供多个程序写入(但同时读取通常是可以的)。
所以在现代通用环境中,是的 - 您确实需要一个操作系统。但即使在今天,仍有一些计算机,例如实时控制器,并不复杂到需要一台。
例如,在 Arduino 环境中,并没有真正的操作系统。当然,构建环境将一堆库代码合并到它构建的每个“二进制”中。但是由于该代码从一个程序到另一个程序没有持久性,因此它不是操作系统。
Art*_*ius 10
我认为许多答案误解了这个问题,归结为:
编译器输出机器码。这个机器码是由CPU直接执行,还是由内核“解释”?
基本上,CPU直接执行机器码。让内核执行所有应用程序会慢得多。但是,有一些注意事项。
当存在操作系统时,应用程序通常被限制执行某些指令或访问某些资源。例如,如果应用程序执行修改系统中断表的指令,CPU 将改为跳转到操作系统异常处理程序,从而终止违规应用程序。此外,通常不允许应用程序读取/写入设备内存。(即“与硬件对话”。)访问这些特殊内存区域是操作系统与显卡、网络接口、系统时钟等设备进行通信的方式。
操作系统对应用程序的限制是通过 CPU 的特殊功能实现的,例如特权模式、内存保护和中断。尽管您在智能手机或 PC 中找到的任何 CPU 都具有这些功能,但某些 CPU 没有。这些 CPU 确实需要特殊的内核来“解释”应用程序代码以实现所需的功能。一个非常有趣的例子是Gigatron,它是一台 8 指令计算机,您可以用模拟 34 指令计算机的芯片构建。
某些语言(如 Java)“编译”为称为字节码的东西,它并不是真正的机器代码。尽管过去它们被解释为运行程序,但现在通常使用称为即时编译的东西,因此它们最终作为机器代码直接在 CPU 上运行。
过去,在虚拟机中运行的软件需要由一个名为Hypervisor的程序“解释”其机器代码。由于行业对虚拟机的巨大需求,CPU 制造商在其 CPU 中添加了 VTx 等功能,以允许 CPU 直接执行来宾系统的大部分指令。但是,当在虚拟机中运行为不兼容CPU设计的软件时(例如,模拟 NES),需要解释机器代码。
当你编译你的代码时,你创建了所谓的“对象”代码(在大多数情况下)依赖于系统库(printf例如),然后你的代码由链接器包装,链接器将添加你的特定操作系统可以的那种程序加载器识别(例如,这就是为什么您不能在 Linux 上运行为 Windows 编译的程序)并知道如何解包您的代码并执行。所以你的程序就像三明治里的一块肉,只能作为一个包吃。
最近我正在阅读内核,我发现程序不能直接访问硬件,而必须通过内核。
嗯,它是对的;如果您的程序是内核模式驱动程序,那么如果您知道如何与硬件“对话”,实际上您可以直接访问硬件,但通常(特别是对于未记录或复杂的硬件)人们使用内核库驱动程序。通过这种方式,您可以找到知道如何以几乎人类可读的方式与硬件通信的 API 函数,而无需知道地址、寄存器、时序和其他一堆东西。
这个机器码中的每条指令是直接从内存中执行(一旦代码被操作系统加载到内存中)还是机器码中的每条命令仍然需要经过操作系统(内核)才能执行
嗯,内核就像一个女服务员,她的职责是把你带到一张桌子上,为你服务。它唯一不能做的 - 它是给你吃的,你应该自己做。与您的代码一样,内核会将您的程序解压缩到内存中,并启动您的代码,这些代码是由 CPU 直接执行的机器代码。内核只需要监督你——你可以做什么,不可以做什么。
它没有说明编译后生成的机器码是直接发给CPU的指令还是需要再次通过内核为CPU创建正确的指令?
编译后生成的机器码是直接发给CPU的指令。毫无疑问。您唯一需要记住的是,并非编译文件中的所有代码都是实际机器/CPU 代码。链接器用一些只有内核可以解释的元数据来包装你的程序,作为一个线索——如何处理你的程序。
机器码加载到内存后会发生什么?它会通过内核还是直接与处理器对话。
如果您的代码只是简单的操作码,例如添加两个寄存器,那么它将在没有内核帮助的情况下由 CPU 直接执行,但是如果您的代码使用库中的函数,那么此类调用将由内核协助,例如女服务员,如果您愿意在餐馆吃饭,他们会给你一个工具——叉子、勺子(它仍然是他们的资产)但是你会用它做什么,——这取决于你的“代码”。
好吧,只是为了防止评论中的火焰 - 我希望它真的是过于简化的模型,我希望可以帮助 OP 理解基本的东西,但是欢迎改进这个答案的好建议。