深入了解如何将内容打印到屏幕上(cout、printf)以及我在教科书上找不到的真正复杂内容的起源

发布于 2024-08-26 21:11:50 字数 214 浏览 10 评论 0 原文

我一直想知道这个问题,但至今还没有找到答案。每当我们使用“cout”或“printf”时,它们到底是如何打印在屏幕上的?文本是如何显示出来的......(这里可能是一个相当模糊的问题,无论你给我什么,我都会工作。)。那么基本上这些功能是如何制作的?...是汇编吗?如果是的话,从哪里开始?这带来了更多的问题,比如他们到底是如何制作 openGl/directx 函数的……

分解它,人们分解它。:)

I've always wondered this, and still haven't found the answer. Whenever we use "cout" or "printf" how exactly is that printed on the screen?. How does the text come out as it does...(probably quite a vague question here, ill work with whatever you give me.). So basically how are those functions made?..is it assembly?, if so where does that begin?. This brings on more questions like how on earth have they made openGl/directx functions..

break it down people break it down.:)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

墨离汐 2024-09-02 21:11:50

下面是一种使用缩写的场景:

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

令人惊奇的是,熊竟然会跳舞。

Here's one scenario, with abbreviations:

  1. printf or cout put characters into a buffer in the user program's address space.
  2. Eventually the buffer fills, or perhaps printf asks for the buffer to be emptied early. Either way, the I/O library calls the operating system, which copies the contents of the buffer to its own space.
  3. Supposing that the output file is bound to a terminal, the operating system delivers the characters to the terminal application.
  4. The terminal app decides that for each character in the buffer, it needs to paint pixels on the screen.
  5. The terminal app sets up pixel-painting instructions, and it asks a window manager to do this on its behalf. (On Unix these days this is usually an X server.)
  6. The window manager takes the pixels. If the window is actually visible on the screen, the window manager then updates a buffer (called the frame buffer) which holds the visible pixels. The window manager may then notify the operating system, or more likely, the window manager is in cahoots with the operating system and they are sharing the same memory.
  7. The next time the screen is refreshed, the hardware sees the new bits in the frame buffer, and it paints the screen differently.
  8. Voilà! You have characters on the screen.

It is amazing that the bear dances at all.

温柔少女心 2024-09-02 21:11:50

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

这些函数可以是汇编语言,也可以是 C 语言,它不会有太大变化(而且,无论如何,您几乎可以用 C 语言完成汇编语言中可以做的任何事情。) 神奇的事情最终发生在软件和硬件的接口上 -- 如何从 printfcout << 到达那里可以像一些指针操作一样微不足道(请参阅下面的 286 示例,或阅读有关 cprintf 进一步向下),或者像在最终到达显示硬件之前经历多层不同的系统调用一样复杂,甚至可能通过网络。

想象一下以下场景

  1. 我从灰尘中挖出我的旧 286 并启动 MS-DOS;我在 真实模式 下编译并运行以下程序:

    void main(void) {
      远长* pTextBuf = (远长*)0xb8000L;
      /* 穷人的 Gotoxy+cprintf 模仿 -- 显示 "C:" (0x43,0x3a)
         屏幕左上角的黑底银字 */
      *pTextBuf = 0x073a0743L;
    }
    
  2. 我正在连接我的笔记本电脑Windows 超级终端连接到我的串行端口,该串行端口通过电缆连接到 SUN 盒子的背面,通过它我可以访问我的 SUN 盒子的控制台。我从该控制台 ssh 到网络上的另一个盒子,在其中运行执行 printf 的程序,通过 更多printf 信息通过管道传输到 more,然后通过 SSH 伪终端通过网络到达我的 SUN 盒子,从那里通过串行电缆到达我的笔记本电脑,通过 Windows 的 GDI 文本绘制功能,最终出现在我的屏幕上。

诺曼的回答添加更多详细信息,希望更多地朝着您原来问题的方向:

  • printfcout << 通常执行对 stdout 的写入——通常是缓冲写入,但并不总是如此案件
    • 过去,不同的编译器供应商(Borland、Microsoft),尤其是 DOS 上的编译器供应商,为您提供了诸如 cprintf,它直接写入视频内存而不进行任何系统调用,memcpy-风格(参见上面我的 286 示例)——更多内容请参见下面
  • 写入 stdout 是一个系统调用,可以是 写入在*nix下,WriteFileWriteConsole Windows 下,DOS 下的 INT 21, 9
  • 通过 stdout 抽象的优势< /strong> 是它允许操作系统执行一些内部管道操作并执行重定向(可以是到 tty 描述符、管道、文件、串行端口、通过套接字到另一台机器等)
    • 它还间接使得多个应用程序的 stdout 可以在同一屏幕上共存,例如在不同的窗口中 - 如果每个应用程序都尝试直接写入,那么这将很难做到单独到视频内存(就像 DOS 上的 cprintf 所做的那样 - 不是今天所谓的真正的或可用的多任务操作系统。)

  • 现在,图形应用程序,例如 rxvt 控制台窗口应用程序、PuTTY telnet/ssh 客户端、Windows 控制台等将:
    • 读取应用程序的stdout
      • rxvt 或 Windows 控制台的情况下,来自 tty 描述符(或等效描述符)
      • 如果您使用 Realterm 之类的工具连接到嵌入式系统或较旧的 SUN box 控制台,则可以通过串行端口
      • 如果您使用 PuTTY 作为 telnet 客户端,则来自套接字
    • 通过以图形方式、逐个像素地呈现信息来显示信息,并将其放入图形应用程序的窗口缓冲区/设备上下文等中。
      • 这通常是通过另一层抽象和系统调用(例如 GDI、OpenGL 等)来完成
      • 像素信息最终存放在线性帧缓冲区中,即专用的内存范围(早在 8MHz CPU 时代,早在 AGP 之前,该区域可以驻留在系统 RAM 中,现在它可能是兆字节和兆字节 显卡本身的双端口 RAM
      • 显卡(以前称为RAMDAC),将定期读取帧缓冲内存范围(例如,当您的 VGA 适配器设置为 60Hz 时,每秒 60 次),逐行扫描(可能执行调色板查找),并将其作为模拟或数字电信号传输到显示器


  • ,甚至在今天,当您以单次启动 *nix 盒子时- 用户模式或在 Windows 控制台中进入全屏,您的图形适配器实际上处于 文本模式
    • 不是线性帧缓冲区,而是(无论是 cprintf 实现还是操作系统)写入更小的 80x25 或 80x50 等。 文本缓冲区数组,其中(例如在 VGA 的情况下)只需要两个字节来编码每个字符值例如 A(1 个字节)以及它的颜色属性(1 个字节)——即它的前景( 4位,或3位+亮度位)和背景颜色(4位,或3位+闪烁位)
    • 对于每条扫描线上的每个像素,RAMDAC:
      • 将跟踪该像素属于哪个文本列和哪个文本行
      • 会查找该列/行位置的字符值和属性
      • 将根据简单位图字体定义查找字符值
      • 将查看字符值的字形位图定义中正在渲染的像素是否应设置为前景或背景,以及基于该位置的字符属性的颜色
      • 如果设置了闪烁位或光标显示且位于当前位置,则可能在偶数秒内翻转前景和背景
      • 绘制像素

会在偶数秒内翻转前景和背景,从 视频卡的历史GPU 页面,更深入地了解我们如何取得今天的成就。

另请参阅 GPU 工作原理显卡工作原理

So basically how are those functions made?..is it assembly?, if so where does that begin?. This brings on more questions like how on earth have they made openGl/directx functions.

Those functions can be assembly or C, it doesn't change much (and, anyway, you can do in C virtually anything you can do in assembly.) The magic ultimately happens at the interface of software and hardware -- how you get there from printf and cout << can be as trivial as a few pointer operations (see the 286 example below, or read about cprintf further down), or as complex as going through multiple layers of diverse system calls, possibly even going over networks, before eventually hitting your display hardware.

Imagine the following scenarios:

  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.

Adding more detail to Norman's answer, hopefully more in the direction of your original question:

  • printf and cout << usually perform writes to stdout -- typically buffered writes, but that has not always been the case
    • back in the day, various compiler vendors (Borland, Microsoft), especially on DOS, provided you with functions like cprintf, which wrote directly to video memory without making any system calls, memcpy-style (see my 286 example above) -- more on that further down
  • writing to stdout is a system call, be it write under *nix, WriteFile or WriteConsole under Windows, INT 21, 9 under DOS, etc.
  • the advantage of going through the stdout abstraction is that it allows the operating system to do some internal plumbing and perform redirection (be it to a tty descriptor, to a pipe, to a file, to a serial port, to another machine via a socket etc.)
    • it also indirectly makes it possible to have multiple applications' stdouts coexist on the same screen, e.g. in different windows -- something that would be much harder to do if each application tried to write directly to video memory on its own (like cprintf did on DOS -- not what would be called today a true or usable multi-tasking operating system.)
  • nowadays, a graphical application such as your rxvt console window application, PuTTY telnet/ssh client, Windows console, etc. will:
    • read your application's stdout:
      • from a tty descriptor (or equivalent) in the case of rxvt or of the Windows console
      • from a serial port if you are using something like Realterm to connect to an embedded system or to an older SUN box console
      • from a socket if you are using PuTTY as a telnet client
    • display the information by rendering it graphically, pixel by pixel, into the graphical application's window buffer/device context/etc.
      • this is typically done through yet another layer of abstraction and system calls (such as GDI, OpenGL etc.)
      • the pixel information ultimately ends up in a linear frame buffer, that is, a dedicated memory range (back in the days of 8MHz CPUs, well before AGP, this area could reside in system RAM, nowadays it could be megabytes and megabytes of dual-port RAM on the video card itself)
      • the video card (what used to be called a RAMDAC), would periodically read the frame buffer memory range (e.g. 60 times a second when your VGA adapter was set for 60Hz), scanline after scanline (possibly doing palette lookups too), and transmit it to the display as either analogue or digital electrical signals
  • back in the day, or even today when you boot your *nix box in single-user mode or go full-screen in a Windows console, you graphics adapter is actually in text mode
    • instead of a liner frame buffer, one (be it the cprintf implementation or the OS) writes to the much smaller 80x25 or 80x50 etc. text buffer array, where (e.g. in the case of VGA) only two bytes are necessary to encode each character value such as A or or (1 byte) as well as its color attributes (1 byte) -- that is, its foreground (4 bits, or 3 bits + brightness bit) and background colors (4 bits, or 3 bits + blink bit)
    • for each pixel on each scanline, the RAMDAC:
      • would keep track of which text column and which text row that pixel belongs to
      • would look up that column/row position's character value and attributes
      • would look the character value against a simple bitmap font definition
      • would see whether the pixel being rendered, in the character value's glyph bitmap definition, should be set to foreground or background, and what color that would be based on the character attribute at that position
      • possibly flip the foreground and background on even seconds if the blink bit was set or the cursor is showing and is at the current position
      • draw the pixel

Start at the History of Video Cards and GPU pages on Wikipedia for a more in-depth look at how we got where we are today.

Also look at How GPUs Work and How Graphic Cards Work.

雪若未夕 2024-09-02 21:11:50

好吧,他们经历了一堆库函数,最终调用了 write() 系统调用,该调用将数据发送到适当的文件描述符,然后导致它出现在终端模拟器中的 read() 调用中(或命令窗口 shell,如果这是 Windows)。终端/shell 可能通过更多的系统调用将数据发送到图形系统,从而将数据绘制在屏幕上。

Windows 和 Unix/Linux 的术语有很大不同,尤其是 shell 的概念在两者中完全不同。但这两种情况下 read() 和 write() 调用的使用非常相似。

系统调用是导致内核执行特定操作的特殊函数;它们的实现方式非常神奇,并且非常依赖于您拥有的处理器类型,但通常是通过导致内核必须清理的某种可恢复的处理器错误来实现的。

Well, they go through a bunch of library functions, and eventually end up calling a write() system call, that sends the data to the appropriate file descriptor, which then causes it to turn up in a read() call in the terminal emulator (or command window shell, if this is Windows). The terminal/shell causes that data to be painted on the screen, probably by way of a bunch more system calls to send it to the graphics system.

Windows and Unix/Linux terminology is quite different, especially the concept of a shell is not at all the same thing in each. But the use of read() and write() calls is pretty similar in both cases.

System calls are special functions that cause the kernel to do specific things; how they're implemented is pretty magical, and very dependent on what sort of processor you have, but usually it's by causing some kind of recoverable processor error that the kernel has to tidy up.

耳钉梦 2024-09-02 21:11:50

破解打开 glibc 的源代码并亲自查看。

简短的回答,大量的 C 代码,偶尔散布一些汇编程序。

Crack open the source to glibc and see for yourself.

Short answer, a lot of C code, sprinkled occasionally, with some assembler.

人间不值得 2024-09-02 21:11:50

魔法确实发生在设备驱动程序中。操作系统为应用程序程序员提供了一个可以连接的接口。这会得到一些处理(例如缓冲),然后发送到设备。然后,设备采用通用表示并将其转换为特定设备可以理解的信号。因此,ASCII 会以某种合理的格式显示在控制台上,或者以适合该设备的形式显示为 PDF 文件、打印机或磁盘。尝试驱动程序无法理解的 ASCII(或 UTF8)之外的其他内容,您就会明白我在说什么。

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

每个设备的驱动程序都不同。但就与操作系统的交互方式而言,每个设备都是相同的,至少对于每一类设备(磁盘、NIC、键盘等)而言是如此。

The magic really happens in the device driver. The OS presents an interface for application programmers to hook into. This gets massaged somewhat (e.g. buffered) and then sent to the device. The device then takes the common representation and transforms it into signals the particular device can understand. So ASCII gets displayed in somme reasonable format on the console, or to a PDF file, or to a printer, or to disk, in the form appropriate for that device. Try something other than ASCII (or UTF8) that the driver does not understand and you will see what I am talking about.

For things the OS cannot handle (special graphics cards for example) the app writes the data directly to device memory. This is how something like DirectX works (to drastically oversimplify).

Each device driver is different. But each is the same in terms of how they interface with the OS, at least for each class of device (disk, NIC, keyboard, etc).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文