深入了解如何将内容打印到屏幕上(cout、printf)以及我在教科书上找不到的真正复杂内容的起源
我一直想知道这个问题,但至今还没有找到答案。每当我们使用“cout”或“printf”时,它们到底是如何打印在屏幕上的?文本是如何显示出来的......(这里可能是一个相当模糊的问题,无论你给我什么,我都会工作。)。那么基本上这些功能是如何制作的?...是汇编吗?如果是的话,从哪里开始?这带来了更多的问题,比如他们到底是如何制作 openGl/directx 函数的……
分解它,人们分解它。:)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
下面是一种使用缩写的场景:
printf
或cout
将字符放入用户程序地址空间的缓冲区中。令人惊奇的是,熊竟然会跳舞。
Here's one scenario, with abbreviations:
printf
orcout
put characters into a buffer in the user program's address space.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.It is amazing that the bear dances at all.
这些函数可以是汇编语言,也可以是 C 语言,它不会有太大变化(而且,无论如何,您几乎可以用 C 语言完成汇编语言中可以做的任何事情。) 神奇的事情最终发生在软件和硬件的接口上 -- 如何从
printf
和cout <<
到达那里可以像一些指针操作一样微不足道(请参阅下面的 286 示例,或阅读有关cprintf
进一步向下),或者像在最终到达显示硬件之前经历多层不同的系统调用一样复杂,甚至可能通过网络。想象一下以下场景:
我从灰尘中挖出我的旧 286 并启动 MS-DOS;我在 真实模式 下编译并运行以下程序:
我正在连接我的笔记本电脑Windows 超级终端连接到我的串行端口,该串行端口通过电缆连接到 SUN 盒子的背面,通过它我可以访问我的 SUN 盒子的控制台。我从该控制台 ssh 到网络上的另一个盒子,在其中运行执行
printf
的程序,通过更多
。printf
信息通过管道传输到more
,然后通过 SSH 伪终端通过网络到达我的 SUN 盒子,从那里通过串行电缆到达我的笔记本电脑,通过 Windows 的 GDI 文本绘制功能,最终出现在我的屏幕上。向诺曼的回答添加更多详细信息,希望更多地朝着您原来问题的方向:
printf
和cout <<
通常执行对stdout
的写入——通常是缓冲写入,但并不总是如此案件cprintf
,它直接写入视频内存而不进行任何系统调用,memcpy
-风格(参见上面我的 286 示例)——更多内容请参见下面stdout
是一个系统调用,可以是写入
在*nix下,WriteFile
或WriteConsole
Windows 下,DOS 下的 INT 21, 9 等stdout
抽象的优势< /strong> 是它允许操作系统执行一些内部管道操作并执行重定向(可以是到 tty 描述符、管道、文件、串行端口、通过套接字到另一台机器等)cprintf
所做的那样 - 不是今天所谓的真正的或可用的多任务操作系统。)rxvt
控制台窗口应用程序、PuTTY telnet/ssh 客户端、Windows 控制台等将:stdout
:rxvt
或 Windows 控制台的情况下,来自 tty 描述符(或等效描述符)cprintf
实现还是操作系统)写入更小的 80x25 或 80x50 等。 文本缓冲区数组,其中(例如在 VGA 的情况下)只需要两个字节来编码每个字符值例如A
或▒
或♣
(1 个字节)以及它的颜色属性(1 个字节)——即它的前景( 4位,或3位+亮度位)和背景颜色(4位,或3位+闪烁位)会在偶数秒内翻转前景和背景,从 视频卡的历史 和 GPU 页面,更深入地了解我们如何取得今天的成就。
另请参阅 GPU 工作原理 和 显卡工作原理。
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
andcout <<
can be as trivial as a few pointer operations (see the 286 example below, or read aboutcprintf
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:
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:
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 throughmore
. Theprintf
information has traveled through a pipe throughmore
, 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
andcout <<
usually perform writes tostdout
-- typically buffered writes, but that has not always been the casecprintf
, which wrote directly to video memory without making any system calls,memcpy
-style (see my 286 example above) -- more on that further downstdout
is a system call, be itwrite
under *nix,WriteFile
orWriteConsole
under Windows, INT 21, 9 under DOS, etc.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.)stdout
s 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 (likecprintf
did on DOS -- not what would be called today a true or usable multi-tasking operating system.)rxvt
console window application, PuTTY telnet/ssh client, Windows console, etc. will:stdout
:rxvt
or of the Windows consolecprintf
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 asA
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)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.
好吧,他们经历了一堆库函数,最终调用了 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.
破解打开 glibc 的源代码并亲自查看。
简短的回答,大量的 C 代码,偶尔散布一些汇编程序。
Crack open the source to glibc and see for yourself.
Short answer, a lot of C code, sprinkled occasionally, with some assembler.
魔法确实发生在设备驱动程序中。操作系统为应用程序程序员提供了一个可以连接的接口。这会得到一些处理(例如缓冲),然后发送到设备。然后,设备采用通用表示并将其转换为特定设备可以理解的信号。因此,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).