屏幕输出的实现方式(cout、printf)以及一些我在教科书中找不到答案的复杂问题的起源探究。

23

我一直想知道这个问题,但仍然没有找到答案。每当我们使用 "cout" 或 "printf" 时,它们是如何在屏幕上打印出来的呢?文本是如何呈现的...(可能是一个相当模糊的问题,我会尽力回答你的问题)。因此,这些函数是如何制作的呢?..是汇编吗?如果是,那么从哪里开始呢?这又引发了更多的问题,比如openGL/directx函数是如何制作的...

把它分解开来吧。


1
相关:来自无libc世界的问候!http://blog.ksplice.com/2010/03/libc-free-world/ - jfs
1
相关视频: "ICU64: C64模拟器的实时黑客攻击" http://www.youtube.com/watch?v=tjcvR5McmSg - jfs
5个回答

23

以下是一个使用缩写的情景:

  1. printfcout 将字符放入用户程序内存空间中的缓冲区。
  2. 最终缓冲区填满,或者printf要求尽早清空缓冲区。无论哪种情况,I/O库都会调用操作系统,将缓冲区内容复制到操作系统自己的空间。
  3. 假设输出文件与终端绑定,操作系统将把字符传递给终端应用程序。
  4. 终端应用程序决定对缓冲区中的每个字符进行屏幕上的像素绘制。
  5. 终端应用程序设置像素绘制指令,并请求窗口管理器代表其执行此操作。(在Unix系统中,通常是X服务器完成此任务。)
  6. 窗口管理器接收像素。如果该窗口实际上可见于屏幕,则窗口管理器随后更新一个称为帧缓冲区的缓冲区,其中保存了可见像素。窗口管理器可能随后通知操作系统,或更可能的是,窗口管理器与操作系统串通,共享同一片内存。
  7. 下次刷新屏幕时,硬件会看到帧缓冲区中的新位,从而以不同的方式绘制屏幕。
  8. 完美!你在屏幕上看到了字符。

这只熊能跳舞真是太神奇了。


然后整个字体的东西还要添加几十个更复杂的步骤! - Martin Beckett
除非您的适配器处于老式文本模式,否则不会出现这种情况。;) - vladr
我认为你忽略了最有趣/复杂/模糊的部分:终端处理。直到今天,它对我来说仍然是一个谜。 - Alex B
我在哪里可以找到有关您列出的不同部分的更多信息? - silent
@sil3nt:去看一本好的操作系统教材可能是不错的选择。我已经有将近15年没有教这门课了,也不知道现在哪些书比较好,但我认为人们仍然喜欢安迪·塔能鲍姆在这个领域的著作。 - Norman Ramsey

19
基本上这些函数是由汇编或C语言编写的,但并不影响太多(而且,无论如何,在C中你可以做到在汇编中可以做的几乎所有事情)。魔法最终发生在软件和硬件的接口处——从printf和cout<<到达那里可能只需要一些指针操作(见下面的286示例或更深入地阅读cprintf),也可能需要通过多个不同系统调用的层次结构,甚至可能通过网络,在最终到达显示硬件之前。想象以下场景:
  1. I dig up my old 286 from under the dust and fire up MS-DOS; I compile and run the following program in real mode:

    void main(void) {
      far long* pTextBuf = (far long*)0xb8000L;
      /* Poor man's gotoxy+cprintf imitation -- display "C:" (0x43,0x3a) in
         silver-on-black letters in the top-left corner of the screen */
      *pTextBuf = 0x073a0743L;
    }
    
  2. I am connecting with my laptop's Windows HyperTerminal to my serial port, which is hooked up with a cable to the back of a SUN box, through which I can access my SUN box's console. From that console I ssh into another box on the network, where I run my program which does printf, piping its output through more. The printf information has traveled through a pipe through more, then through an SSH pseudo-tty through the network to my SUN box, from there through the serial cable onto my laptop, through Windows' GDI text drawing functions before finally appearing on my screen.

Norman's answer的基础上增加更多细节,希望能更接近你最初的问题:

  • printfcout<<通常会在stdout上执行写入操作,通常是缓冲写入,但不总是如此。
  • 早期的各种编译器供应商(Borland、Microsoft)特别是在DOS上提供了像cprintf这样的函数,它们可以直接写入视频内存而不进行任何系统调用,类似于memcpy风格(参见我上面的286示例)--更多信息请看下文。
  • 写入stdout是一个系统调用,无论是*nix下的write,Windows下的WriteFileWriteConsole,还是DOS下的INT 21, 9等。
  • 通过stdout抽象的优点是它允许操作系统进行一些内部管道连接并执行重定向(无论是到tty描述符、管道、文件、串口、通过套接字连接到另一台机器等)。
  • 现在,图形应用程序例如您的rxvt控制台窗口应用程序、PuTTY telnet/ssh客户端、Windows控制台等会:
  • 读取您的应用程序的stdout
  • 从tty描述符(或等效物)中读取,如果是Windows控制台或rxvt的情况
  • 如果您使用类似于Realterm连接到嵌入式系统或旧SUN盒子控制台,则从串口读取
  • 如果您正在使用PuTTY作为telnet客户端,则从套接字读取
  • 通过逐像素地渲染将信息显示到图形应用程序的窗口缓冲区/设备上下文等。
  • 这通常是通过另一层抽象和系统调用(例如GDI、OpenGL等)完成的。
  • 像素信息最终会进入线性帧缓冲区,即一个专用的内存范围(在8MHz CPU时代之前,这个区域可能驻留在系统RAM中,现在它可能是视频卡本身的兆字节级双口RAM
  • 视频卡(曾经被称为RAMDAC)会定期读取帧缓冲区内存范围(例如当您的VGA适配器设置为60Hz时,每秒读取60次),逐行扫描(可能还进行调色板查找),并将其作为模拟或数字电信号传输到显示器
  • 回到早期,或者即使在今天,当您在单用户模式下启动*nix框或全屏显示Windows控制台时,您的图形适配器实际上处于文本模式
  • 与其写入线性帧缓冲区,在这种情况下(无论是cprintf实现还是操作系统),都将写入更小的80x25或80x50等文本缓冲区数组,其中(例如在VGA的情况下)仅需要两个字节来编码每个字符值,例如A(1字节)以及其颜色属性(1字节)--也就是说,它的前景(4位或3位+亮度位)和背景颜

    从维基百科的显卡历史GPU页面开始,更深入地了解我们今天所处的情况。

    还要查看GPU工作原理图形卡如何工作


打开命令提示符,全屏显示(Alt+Enter),然后输入 debug 命令。在 - 提示符处,输入 f b800:0000 640 23 2e 命令,以黄色底绿色字的 # 符号填充前10行。 - vladr

2
好的,它们通过一系列的库函数,最终调用write()系统调用将数据发送到适当的文件描述符,然后在终端仿真器(或命令窗口shell,如果这是Windows)中的read()调用中导致其出现。终端/ shell使该数据被绘制在屏幕上,可能通过一堆更多的系统调用将其发送到图形系统。
Windows和Unix / Linux术语有很大不同,特别是在每个术语中shell的概念都不同。但是,在这两种情况下使用read()和write()调用非常类似。
系统调用是引起内核执行特定操作的特殊函数;它们的实现非常神奇,并且非常依赖于您拥有的处理器类型,但通常是通过引起某种可恢复的处理器错误来实现的,而内核必须整理好这些错误。

0

打开 glibc 的源代码并自行查看。

简短回答,很多 C 代码,偶尔含有一些汇编语言。


0

真正的魔力发生在设备驱动程序中。操作系统提供了一个接口,供应用程序员挂钩。这个接口会被稍微处理一下(例如缓冲),然后发送到设备。设备会将通用表示转换为特定设备可以理解的信号。因此,ASCII以适合该设备的形式显示在控制台上,或者显示在PDF文件、打印机或磁盘上。尝试使用驱动程序不理解的其他内容(而不是UTF8),您将看到我所说的内容。

对于操作系统无法处理的事物(例如特殊的图形卡),应用程序直接将数据写入设备内存。这就是类似DirectX的东西如何工作的(过度简化)。

每个设备驱动程序都是不同的。但至少对于每种设备类别(磁盘、NIC、键盘等),它们与操作系统的接口方式是相同的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接