12

可能重复:
了解 printf 的硬件

我不是在寻找printf函数的实现,但我想知道,printf在 C 中调用时会发生什么?所有活动都发生在软件和硬件级别。

这就是我认为的 PrintfCAll -> KernelModeOn -> SystemCallMade -> 数据放在某种类型的输出缓冲区上 -> 输出要转储到某些控制器的缓冲区上 -> 控制器将其转储到监视器上 -> 中断 CPU 说工作已经完成。

我有多正确?谢谢。

编辑:Unix 可以作为一个平台。说ubuntu。有人能告诉我数据从哪里流出,还有监视器的控制器吗?上述时间表在多大程度上是正确的?

4

3 回答 3

21

以下是一般性的描述和总结,基于一般的编程概念,而不是任何特定的实现。

调用以printf普通子程序调用开始;不涉及内核模式。在很大程度上,printf是普通代码,可以用 C 编写。大部分printf代码本身与解释格式字符串、将参数转换为要写入的字符串以及将这些字符串写入输出文件有关。大部分工作将通过调用的子例程完成printf,例如将数字(如intor的对象float)转换为数字(表示数字的字符串)的子例程。

printf也可能调用malloc或相关例程来为准备字符串的缓冲区获取内存。我将避免malloc在此答案中描述电话。

所有解释格式字符串、转换参数和准备要编写的字符串的工作都可以在 C 中完成,尽管高质量的库可能会使用各种特定于目标的优化,包括汇编语言,以提高速度或效率。

在某些时候,当printf有一个字符串要打印时,它会调用一个例程将字符串写入stdout. 这可能是fwrite或一些类似的子程序。为了讨论,我假设它是fwrite.

通常,流是缓冲的。因此,当printf调用时fwritefwrite检查它的缓冲区有多满。如果新字符串 fromprintf适合缓冲区,fwrite则只需将字符串添加到缓冲区并返回。如果缓冲区已满,则fwrite调用另一个例程将缓冲区内容实际写入文件。(通常,这涉及使用传入字符串的一部分填充缓冲区,将缓冲区写入文件 [并将缓冲区标记为空],然后将传入字符串的其余部分复制到新的空缓冲区中。)某些其他事情也可能根据情况触发写入缓冲区,例如检测传入字符串中的换行符。

假设要写入缓冲区,fwrite调用系统例程write。面对的write是一个库例程;fwrite执行对 call 的普通子例程调用write。系统例程将有一些部分是普通的子例程,但是,当它们需要完成基本工作时,会有某种系统调用指令(有时称为陷阱)。

当您执行系统调用指令时,处理器会做几件事。它将处理器寄存器保存在指定位置。这包括描述用户进程状态的通用寄存器和特殊寄存器。然后处理器切换到内核模式,这通常涉及设置位以指示新的执行状态是特权(允许更改特殊处理器寄存器,执行特殊指令等)并从其他位置加载寄存器,或将它们设置为已知价值观。特别是,程序计数器(处理器读取指令以执行的位置)被设置为指向一个特定位置,在该位置操作系统有代码来处理系统调用。

现在处理器正在内核模式下执行。通常,此时处理器的工作是尽快退出内核模式,以便它可以恢复进程之间的分时并为其他工作做好准备。此外,现代操作系统有很多层,因此很难准确地说出此时会发生什么。

一种情况是系统调用处理程序(发生系统调用时调用的软件)读取用户进程保存的寄存器和内存以确定进程请求的内容。在每个系统上,都指定了一些将参数传递给系统调用的方法。例如,某个寄存器可能包含一个指示请求是什么的数字(0 表示写入,1 表示读取,2 表示获取当前时间,3 表示更改内存映射等),并且每个请求都会传入某些参数其他寄存器或内存中(一个寄存器可能包含内存中的地址,而另一个包含要写入的长度)。

因此,系统调用处理程序找出正在发出的请求并分派给代码来处理它。这可能涉及收集请求的参数并将它们形成对要完成的工作的描述,然后将该工作放入队列并离开系统调用处理程序。

虽然有工作要做,但操作系统可能不会返回到用户进程。正如我之前提到的,现代操作系统中有很多层。操作系统中有设备驱动程序、内核扩展、微内核、软件库等等。然而,操作系统是有组织的,在某些时候,它决定做系统调用所要求的工作。

在写入标准输出的情况下,工作被发送到“设备驱动程序”,这是处理“设备”工作的软件的名称。最初,设备是连接到系统的硬件。设备驱动程序会将要写入的数据复制到内存中的一个特殊位置,并向设备发出命令(使用特殊指令)从内存中读取该数据并将其发送到设备发送它的任何地方(终端、磁盘驱动器) , 任何)。设备驱动程序的另一部分是工作完成时调用的例程。(此调用类似于系统调用,但通常称为中断。)工作完成后,设备驱动程序会将消息传递回操作系统的其他部分,

今天,许多“设备”是实现虚拟设备的软件。用户进程的标准输出可能是某种伪终端。由于该伪终端没有实际的硬件终端,它必须通过请求其他软件的帮助来处理写入请求。

当伪终端是图形显示器上终端窗口的一部分时,有一些软件可以实现终端窗口。该软件接受写入标准输出的文本,决定将其放置在窗口中的哪个位置,并调用其他软件将字符转换为窗口中像素的变化。也就是说,一些软件正在读取字符,在一些表格和其他数据中查找它们的描述(字体描述等),然后在图像缓冲区中绘制这些字符。

当图像缓冲区准备好时,会调用更多的软件将图像缓冲区写入显示器。同样,这涉及将数据传递给另一个设备驱动程序。最终,它到达一个实际的硬件设备,该设备获取数据并将其显示在显示器上。

总而言之,有一个巨大的事件链。数据通过多个层向上和向下传递,可能涉及几个不同的用户进程和几个不同的设备驱动程序以及许多软件库。很难全面了解整个过程。通常,人们不想尝试一下子了解整个过程,而是会分别了解每个步骤。例如,在我的职业生涯中,有时我不得不处理系统调用指令的微小细节。但是,当考虑我的整个系统如何工作时,我会考虑更大级别的进程相互通信,而不考虑这些通信如何工作的细节。

于 2012-12-01T12:08:14.537 回答
2

C 的printf调用写入程序的标准输出缓冲区。如果连接了控制台/终端,则控制台/终端会读取该数据,并通过视频驱动程序显示它。

于 2012-12-01T08:58:00.707 回答
0

printf 函数不是 C 语言的一部分,因为 C 语言本身没有定义输入或输出。printf 函数只是 C 程序可访问的标准函数库中的一个有用函数。printf 的行为在 ANSI 标准中定义。如果您使用的编译器符合此标准,那么您应该可以使用所有功能和属性。

于 2012-12-01T09:07:03.523 回答