深入了解事物如何印刷到屏幕上(cout,printf)以及我在教科书上似乎找不到的非常复杂的东西的起源

sil*_*ent 23 c++ assembly cout libraries

我一直想知道这一点,但仍未找到答案.每当我们使用"cout"或"printf"时,屏幕上印刷的是什么?文本是如何形成的......(这里可能是一个非常模糊的问题,无论你给我什么,我都会做错.).那么基本上这些功能是如何制作的?它是汇编吗?如果是的话,它从哪里开始?这带来了更多问题,比如他们如何制作openGl/directx函数.

分解人们将其分解.:)

Nor*_*sey 22

这是一个带有缩写的场景:

  1. printf或者cout将字符放入用户程序地址空间的缓冲区中.
  2. 最终缓冲区填满,或者可能printf要求提前清空缓冲区.无论哪种方式,I/O库都会调用操作系统,该操作系统会将缓冲区的内容复制到自己的空间.
  3. 假设输出文件绑定到终端,操作系统将字符传递给终端应用程序.
  4. 终端应用程序决定对缓冲区中的每个字符,它需要在屏幕上绘制像素.
  5. 终端应用程序设置像素绘制指令,并要求窗口管理器代表它执行此操作.(在Unix上,这些天通常是X服务器.)
  6. 窗口管理器获取像素.如果窗口实际上在屏幕上可见,则窗口管理器然后更新保持可见像素的缓冲区(称为帧缓冲区).然后,窗口管理器可以通知操作系统,或者更可能的是,窗口管理器与操作系统相关并且它们共享相同的存储器.
  7. 下次刷新屏幕时,硬件会看到帧缓冲区中的新位,并以不同方式绘制屏幕.
  8. 瞧!屏幕上有字符.

令人惊讶的是,熊在跳舞.


vla*_*adr 19

那么基本上这些功能是如何制作的?它是汇编吗?如果是的话,它从哪里开始?这带来了更多的问题,例如他们如何制作openGl/directx函数.

这些功能可以是汇编或C,它不会有太大变化(无论如何,你可以在C中做几乎任何你可以在汇编中做的事情.) 魔术最终发生在软件和硬件的界面 - 你如何到达那里来自printf并且cout <<可以像几个指针操作一样简单(参见下面的286示例,或者cprintf进一步了解),或者在最终点击显示硬件之前,通过多层系统调用,甚至可能通过网络进行复杂.

想象一下以下场景:

  1. 我从尘土中挖出旧的286并点燃了MS-DOS; 我在实模式下编译并运行以下程序:

    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;
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 我将笔记本电脑的Windows HyperTerminal连接到我的串口,该端口连接着一个SUN盒背面的电缆,通过它我可以访问我的SUN盒控制台.从那个控制台我ssh到网络上的另一个盒子,在那里我运行我的程序printf,管道输出通过more.该printf信息已经通过管道通过旅行more通过网络,通过串行电缆终于出现在我的屏幕上之前,然后通过SSH伪终端,以我的太阳箱,从那里到我的笔记本电脑,通过Windows的GDI文本绘图函数.

诺曼的答案添加更多细节,希望更多地朝着原始问题的方向发展:

  • printf并且cout <<通常执行写入stdout - 通常是缓冲写入,但情况并非总是如此
    • 在当天,各种编译器供应商(Borland,Microsoft),特别是在DOS上,为您提供了类似的功能cprintf,它直接写入视频内存而不进行任何系统调用,memcpy-style(参见我上面的286示例) - 更多内容再向下
  • 写入stdout是一个系统调用,无论是write在*nix下,WriteFile还是WriteConsole在Windows下,在UNIX 下的INT 21,9等等.
  • 通过stdout抽象的优点是它允许操作系统做一些内部管道并执行重定向(无论是tty描述符,管道,文件,串行端口,通过套接字等到另一台机器等.)
    • 它还间接地使多个应用程序stdout在同一屏幕上共存成为可能,例如在不同的窗口中 - 如果每个应用程序都试图直接写入视频内存(就像cprintf在DOS上那样),那就更难了- 不是今天所谓的真正的或可用的多任务操作系统.)
  • 如今,诸如控制台窗口应用程序,PuTTY telnet/ssh客户端,Windows控制台等图形应用程序rxvt将:
    • 阅读你的申请表stdout:
      • 来自tty描述符(或等效的)在rxvtWindows控制台的情况下
      • 如果您使用类似Realterm的东西连接到嵌入式系统或旧的SUN盒控制台,请从串行端口获取
      • 如果您使用PuTTY作为telnet客户端,请从套接字
    • 通过逐个像素地以图形方式将信息呈现到图形应用程序的窗口缓冲区/设备上下文等中来显示信息.
      • 这通常是通过另一层抽象和系统调用(例如GDI,OpenGL等)完成的.
      • 像素信息最终以线性帧缓冲区结束,即专用存储器范围(在8MHz CPU的时代,在AGP之前,这个区域可以驻留在系统RAM中,现在它可能是兆字节和兆字节的双 -视频卡本身的端口RAM)
      • 视频卡(以前称为RAMDAC)会周期性地读取帧缓冲存储器范围(例如,当VGA适配器设置为60Hz时,每秒60次),扫描线后扫描线(可能也进行调色板查找),以及传输它作为模拟或数字电信号显示在显示器上
  • 回到白天,甚至今天,当您在单用户模式下启动*nix框或在Windows控制台中全屏显示时,您的图形适配器实际上处于文本模式
    • 而不是一个线性帧缓冲器,一个(无论是cprintf实现还是OS)写入更小的80x25或80x50等文本缓冲器阵列,其中(例如在VGA的情况下)只需要两个字节来编码每个字符值例如A??(1字节)以及它的颜色属性(1字节) - 即它的前景(4位,或3位+亮度位)和背景颜色(4位,或3位+闪烁位)
    • 对于每条扫描线上的每个像素,RAMDAC:
      • 将跟踪哪个文本列和该像素属于哪个文本行
      • 会查找列/行位置的字符值和属性
      • 会针对简单的位图字体定义查看字符值
      • 将查看在字符值的字形位图定义中呈现的像素是否应设置为前景或背景,以及基于该位置的字符属性的颜色
      • 如果闪烁位已设置或光标正在显示且位于当前位置,则可能在偶数秒内翻转前景和背景
      • 绘制像素

从Wikipedia上的视频卡GPU页面历史开始,更深入地了解我们今天的状态.

另请参阅GPU如何工作以及图形卡如何工作.