我在IRC频道上问了这个问题,遗憾的是,我正在绕圈子。
我的目标是对stdio.h中的printf()这样的函数如何与Windows操作系统“对话”进行高层次的概述(但必要时提供一些技术细节)。
我了解一些关于MSVCRT.dll的知识,Windows的一些内容,比如它是如何与内核32.dll通信的,后者是ntdll.dll中的Native。我认为VisualStudio使用msvcrt100.dll作为C标准库.
有人建议使用一个开放源码C标准库来理解引擎盖下发生的事情。除了不知道如何在我的Visual项目中使用它之外,我仍然不知道它如何与操作系统通信。
我的可怕理解错过了几个步骤,具体如下:
1)对printf()的语法与头stdio.h2进行检查)在运行程序时,它使用msvcrt100对printf 3) msvcrt100,然后加载必要的kernel32 4) Kernel32,然后将它传递给ntdll.dll
这个问题集中在C上,但是如果C++是相同的,那么可以随意发布。
只有窗户。
发布于 2018-05-30 14:44:07
虽然具体细节因操作系统而异,但您可能希望从以下几个方面开始:
这些(基于磁盘的)文件格式包含机器代码、初始程序数据值、重新定位和符号表的部分。
在硬件执行代码之前,所有符号描述的值(即main、printf)都必须完全解析为内存位置。
编译器(和汇编程序)在编译时有关于各种代码和数据的最终内存位置的不完整信息--因此,它们在对象文件中象征性地共享该信息,而不是作为最终的内存地址--这就是重新定位和符号表发挥作用的地方。
符号表有导入和导出-导出给对象文件各节(代码/数据)中的偏移量提供字符串名称;而导入只与字符串名称相关联(外部名称用于以后的解析)。
一旦知道符号的内存地址,重新定位就告诉对象文件的使用者如何&在哪里修复机器代码和数据;因此,重定位通常有对符号表中条目的引用(它们也引用对象模块中的部分.)。
程序的.exe文件有一个特殊位置-- main标记为可执行文件头中的入口点。程序的.exe也以某种方式解析了所有外部引用--然而,生成可执行文件的链接器仍然没有关于所有符号的内存地址的完整信息,因为这通常要到加载时才会发生。因此,.exe文件仍然具有重定位和符号表。
.exe将所有.obj文件合并成一个文件:代码部分与数据部分连接在一起。与.obj文件相比,obj文件的某些重定位可以被解析,从而从.exe文件中删除;其他重定位可以简化它们的形式(引用代码或数据部分,而不是引用特定的符号--这使得重新定位条目更短)。
通常,操作系统加载程序最终确定代码和数据部分的位置,从而完成对符号的内存地址分配,这意味着任何未完成的重定位现在都可以执行。
DLL ( .dll )文件与.exe文件类似,只不过它有特定的导出。由链接器创建的.exe文件可能具有对.dll文件的引用。这些引用由动态链接加载器读取,因此加载.exe文件的操作还需要加载.dll文件,而.dll文件可能还需要加载额外的.dll文件。所有这些文件都根据重新定位条目相互交叉链接。
然后,硬件执行机器代码指令(如call printf ),其中对printf的引用是由其内存位置指定的。有许多方法,有些涉及跳转表或几条指令的代码序列;然而,只需说在执行机器代码期间,硬件只看到/知道内存地址,而不知道符号名称。所有符号引用最终都由重定位和符号表条目系统解析为内存地址。
当适当时(例如本地缓冲区满),printf将最终调用某种类型的系统呼叫来执行i/o,例如将本地缓冲区刷新到磁盘或设备。系统调用是用户进程请求操作系统操作的一种方式。系统调用与常规调用类似,只是调用方在用户进程中,被调用者是操作系统入口点。系统调用提供了提高权限(从用户到内核)的受控方法,以提供访问共享设备所需的权限。系统调用不使用内存地址。但是,每个系统调用都与一个简单的整数索引相关联--这允许调用操作系统的..exe和..dll不知道内核内存地址。
发布于 2018-06-18 12:35:24
您可能希望阅读WriteConsole的文档。与printf最接近的是本机Windows函数。正如您所看到的,它需要一个格式化的字符串--它的工作方式更像puts。在调用sprintf之前,您可能需要先调用WriteConsole。
C++在功能上也有类似的分裂。std::cout最终调用WriteConsole,而operator<<则对单个参数进行格式化。
https://softwareengineering.stackexchange.com/questions/371803
复制相似问题