在C中printf背后的是什么?

可能重复:
了解printf的硬件

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

这就是我认为PrintfCAll – > KernelModeOn – > SystemCallMade – >数据放在某些排序的输出缓冲区 – >输出被转储到某些控制器的缓冲区 – >控制器将其转储到监视器 – >中断CPU说工作已完成。

我有多正确? 谢谢。

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

以下是基于编程概念而非任何具体实现的一般描述和概述。

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

printf也可能调用malloc或相关的例程来获取缓冲区的内存,它准备字符串。 我将不再在这个答案中描述malloc调用。

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

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

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

让我们说,为了写缓冲区, fwrite调用系统例程writewrite的面是一个库例程; fwrite执行普通的子程序调用来调用write 。 系统例程将具有一些普通子例程,但是,当他们需要执行细节工作时,存在某种系统调用指令(有时称为陷阱)。

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

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

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

因此,系统调用处理程序会计算出正在进行的请求,并调度到代码来处理该请求。 这可能涉及收集请求的参数并将它们形成对要完成的工作的描述,然后将该工作放在队列上并离开系统调用处理程序。

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

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

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

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

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

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

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

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