我正在阅读有关如何编写极简主义内核的this教程。我在两者之间读到了这个:
运行时库
为您的操作系统编写代码的主要部分是重写运行时库,也称为libc。这是因为 RTL是编译器软件包中最依赖操作系统的部分:C RTL提供了足够的功能,允许您编写便携式 程序,但RTL的内部工作依赖于操作系统 使用即可。事实上,编译器供应商通常使用不同的RTL 操作系统:Microsoft Visual C ++为各种提供了不同的库 调试/多线程/ DLL和旧的MS-DOS的组合 编译器提供最多6个不同内存的运行时库 模型。
我对这部分感到困惑。假设我用C代码编写内核,并根据建议使用内置的printf()函数来打印内容。最后,我的代码将被翻译成机器代码。当它被执行时,处理器将直接运行它。为什么作者说:
RTL的内部工作依赖于正在使用的操作系统?
答案 0 :(得分:3)
有两个不同的问题:
在内核中运行时printf()
会做什么?因为用于开发内核的C编译器的RTL可能假设有一些运行时环境,包括控制台,操作系统等,所以很可能它会崩溃或什么都不做。即使您使用的是独立的C / C ++实现,运行时可能会接管串行端口或诸如此类的东西来执行输出。您可能不希望这样,因为您的内核驱动程序将控制I / O.因此,您需要从RTL重新实现基础文件I / O.
在运行在内核之上的用户进程中运行时printf()
会做什么?如果内核保护对硬件资源的访问,则它无法执行任何操作。来自RTL的底层文件I / O代码必须知道如何与内核通信以打开标准输入/输出“文件”的任何传递并执行数据交换。
您需要了解您是使用C / C ++编译器+ RTL的独立或托管实现,以及所有含义。对于内核开发,您将使用独立的实现。对于用户空间开发,您需要托管实现(可能是交叉编译器),但运行时库必须编写为托管实现。请注意,在这两种情况下,您都可以使用相同的编译器,只需将其指向适当的头文件和库即可。例如,在Linux上,内核和用户空间开发可以使用完全相同的gcc编译器完成,具有不同的头文件和库。
处理器不知道控制台是什么,或者内核是什么。有些代码必须实际访问硬件。当您从托管的C / C ++实现中获取printf()
时,该实现(在其内部深处的某个位置)将调用它要运行的特定平台的系统调用。该系统调用旨在写入包含“控制台”的一些抽象。在此系统调用的另一端,调用是将此数据推送到某些硬件的内核代码。它甚至可能不是直接硬件,它可能是另一个进程的用户空间。
例如,无论何时在Unix机器上运行基于GUI的终端(KDE的Konsole,X11 xterm,OS X终端等),调用printf()
的用户空间进程都非常非常接近在任何东西击中硬件之前去。即(即使这是简化的!):
printf()
将数据写入缓冲区write()
库函数。write()
库函数调用将控制权转移到内核的系统调用。read()
文件句柄。这会通过库进入系统调用。答案 1 :(得分:1)
Printf()是一个独立于操作系统的高级功能。然而,这只是谜题的一部分,它本身就具有依赖性。它需要能够写入stdout。这将导致低级操作系统相关的系统调用,如create()打开stdout流和write()以发送printf输出。不同的操作系统有不同的系统调用,所以总有一个适应层,你的将会有。
当然,你可以让你的内核中的printf()工作。实际上看到对printf()的调用输出将是真正需要解决的问题。没有什么比内核模式中的终端窗口更好。