《程序员的自我修养--链接、装载与库》学习笔记(一)

2023-11-19

本系列文章是《程序员的自我修养–链接、装载与库》(电子工业出版社)一书的学习摘录笔记,本文是书中1.1至1.4部分。

文章目录

基础概念

#include <stdio.h>
int main()
{
printf("Hello World\n");
return 0;
}

对于以上的程序,我们提出一下问题:

  1. 程序为什么要被编译器编译了之后才可以运行?
  2. 编译器在把C语言程序转换成可以执行的机器码的过程中做了什么,
    怎么做的?
  3. 最后编译出来的可执行文件里面是什么? 除了机器码还有什么? 它们
    怎么存放的, 怎么组织的?
  4. #include <stdio.h>是什么意思? 把stdio.h包含进来意味着什么? C语言
    库又是什么? 它怎么实现的?
  5. 不同的编译器(Microsoft VC、 GCC) 和不同的硬件平台(x86、
    SPARC、 MIPS、 ARM) , 以及不同的操作系统(Windows、 Linux、
    UNIX、 Solaris) , 最终编译出来的结果一样吗? 为什么?
  6. Hello World程序是怎么运行起来的? 操作系统是怎么装载它的? 它从
    哪儿开始执行, 到哪儿结束? main函数之前发生了什么? main函数结束
    以后又发生了什么?
  7. 如果没有操作系统, Hello World可以运行吗? 如果要在一台没有操作
    系统的机器上运行Hello World需要什么? 应该怎么实现?
  8. printf是怎么实现的? 它为什么可以有不定数量的参数? 为什么它能够
    在终端上输出字符串?
  9. Hello World程序在运行时, 它在内存中是什么样子的?

这些问题都需要我们对程序编译、静态链接、操作系统的动态链接及运行库和标准库的实现进行了解。
我们在开始正式学习前需要对计算机系统的一些基本概念进行一些回顾,这包括硬件部分和软件部分。

硬件

对于系统程序开发者来说, 计算机多如牛毛的硬件设备中, 有三个部件最为关键, 它们
分别是中央处理器CPU内存I/O控制芯片, 这三个部件几乎就是计算机的核心了 。

早期的计算机没有很复杂的图形功能, CPU的核心频率也不高, 跟内存的频率一样, 它们都是直接连接在同一个总线(Bus) 上的。 由于I/O设备诸如显示设备、 键盘、 软盘和磁盘等速度与CPU和内存相比还是慢很多, 当时也没有复杂的图形设备, 显示设备大多是只能输出字符的终端。 为了协调I/O设备与总线之间的速度, 也为了能够让CPU能够和I/O设备进行通信, 一般每个设备都会有一个相应的I/O控制器。
在这里插入图片描述
后来由于CPU核心频率的提升, 导致内存跟不上CPU的速度, 于是产生了与内存频率一致的系统总线, 而CPU采用倍频的方式与系统总线进行通信。 接着随着图形化的操作系统普及, 特别是3D游戏和多媒体的发展, 使得图形芯片需要跟CPU和内存之间大量交换数据, 慢速的I/O总线已经无法满足图形设备的巨大需求。 为了协调CPU、 内存和高速的图形设备, 人们专门设计了一个高速的北桥芯片, 以便它们之间能够高速地交换数据。由于北桥运行的速度非常高, 所有相对低速的设备如果全都直接连接在北桥上, 北桥既须处理高速设备, 又须处理低速设备, 设计就会十分复杂。 于是人们又设计了专门处理低速设备的南桥(Southbridge) 芯片,磁盘、 USB、 键盘、 鼠标等设备都连接在南桥上, 由南桥将它们汇总后连接到北桥上。

20世纪90年代的PC机在系统总线上采用的是PCI结构,而在低速设备上采用的ISA总线, 采用PCI/ISA及南北桥设计的硬件构架如下图所示:
在这里插入图片描述
位于中间是连接所有高速芯片的北桥(Northbridge, PCI Bridge) , 它就像人的心脏, 连接并驱动身体的各个部位; 它的左边是CPU, 负责所有的控制和运算, 就像人的大脑。 北桥还连接着几个高速部件, 包括左边的内存和下面的PCI总线。

软件

系统软件这个概念其实比较模糊, 传统意义上一般将用于管理计算机本身的软件称为系统软件, 以区别普通的应用程序。 系统软件可以分成两块, 一块是平台性的, 比如操作系统内核、 驱动程序、 运行库和数以千计的系统工具; 另外一块是用于程序开发的, 比如编译器、 汇编器、 链接器等开发工具和开发库。 书中着重介绍了系统软件的一部分, 主要是链接器和库(包括运行库和开发库) 的相关内容。

在这里插入图片描述
计算机软件体系结构如上图所示。

每个层次之间都须要相互通信,既然需要通信就必须有一个通信的协议,我们一般将其称为接口( Interface),接口的下面那层是接口的提供者,由它定义接口;接口的上面那层是接口的使用者,它使用该接口来实现所需要的功能。在层次体系中,接口是被精心设计过的,尽量保持稳定不变,那么理论上层次之间只要遵循这个接口,任何一个层都可以被修改或被替换。

除了硬件和应用程序,其他都是所谓的中间层,每个中间层都是对它下面的那层的包装和扩展。正是这些中间层的存在,使得应用程序和硬件之间保持相对的独立,比如硬件和操作系统都日新月异地发展,但是最初为80386芯片和DOS系统设计的软件在最新的多核处理器和 WindowsVista下还是能够运行的,这方面归功于硬件和操作系统本身保持了向后兼容性,另一方面不得不归功于这种层次结构的设计方式。最近开始流行的虚拟机技术更是在硬件和操作系统之间增加了层虚拟层,使得一个计算机上可以同时运行多个操作系统,这也是层次结构带来的好处,在尽可能少改变甚至不改变其他层的情况下,新增加一个层次就可以提供前所未有的功能。

软件体系中最上层的是应用程序,比如我们用到的网络浏览器、多媒体播放器等,从整个结构层次上看,开发工具和用用程序是属于同一层次的,因为他们都使用一个接口,那就是操作系统应用程序编程接口(Application Programming Interface),应用程序接口的提供者是运行库,什么样的运行库提供什么样的API,Windows的运行库提供Windows API, 最常见的32位Windows提供的API又被称为Win32。

操作系统内核层对于硬件层来说是硬件接口的使用者,而硬件是接口的定义者,硬件的接口定义决定了操作系统内核,具体来讲就是驱动程序如何操作硬件,如何与硬件进行通信。这种接口往往被叫做硬件规格( Hardware Specification)。

硬件的生产厂商负责提供硬件规格,操作系统和驱动程序的开发者通过阅读硬件规格文档所规定的各种硬件编程接口标准来编写操作系统和驱动程序。

操作系统
操作系统的一个功能是提供抽象的接口, 另外一个主要功能是管理硬件资源。

  1. 操作系统和内存
    现在主流的程序运行模式是多任务(Multi-tasking) 系统, 操作系统接管了所有的硬件资源, 并且本身运行在一个受硬件保护的级别。 所有的应用程序都以进程(Process) 的方式运行在比操作系统权限更低的级别, 每个
    进程都有自己独立的地址空间, 使得进程之间的地址空间相互隔离。CPU由操作系统统一进行分配, 每个进程根据进程优先级的高低都有机会得到CPU, 但是, 如果运行时间超出了一定的时间, 操作系统会暂停该进程, 将CPU资源分配给其他等待运行的进程。 这种CPU的分配方式即所谓的抢占式(Preemptive) , 操作系统可以强制剥夺CPU资源并且分配给它认为目前最需要的进程。 如果操作系统分配给每个进程的时间都很短, 即CPU在多个进程间快速地切换, 从而造成了很多进程都在同时运行的假象。 目前几乎所有现代的操作系统都是采用这种方式。

  2. 操作系统和硬件
    操作系统作为硬件层的上层, 它是对硬件的管理和抽象。

当成熟的操作系统出现以后, 硬件逐渐被抽象成了一系列概念。 在UNIX中, 硬件设备的访问形式跟访问普通的文件形式一样; 在Windows系统中, 图形硬件被抽象成了GDI, 声音和多媒体设备被抽象成了DirectX对象; 磁盘被抽象成了普通文件系统,等等。

繁琐的硬件细节全都交给了操作系统, 具体地讲是操作系统中的硬件驱动(Device Driver) 程序来完成。 驱动程序可以看作是操作系统的一部分, 它往往跟操作系统内核一起运行在特权级, 但它又与操作系统内核之间有一定的独立性, 使得驱动程序有比较好的灵活性。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

《程序员的自我修养--链接、装载与库》学习笔记(一) 的相关文章

  • 向进度条添加百分比文本 C#

    我有一个方法可以显示进程栏何时正在执行以及何时成功完成 我工作得很好 但我想添加一个百分比 如果完成 则显示 100 如果卡在某个地方 则显示更少 我在网上做了一些研究 但我无法适应我正在寻找的解决方案 这是我的代码 private voi
  • 在 C++ 中使用 matlab 结构(matlab 函数调用的返回值)(由 matlab 编译器生成的库)

    你好 我有一个相当简单的 matlab 函数 例如 function MYSTRUCT myfunc MYSTRUCT prop1 test MYSTRUCT prop2 foo MYSTRUCT prop3 42 end 我用 matla
  • 注销租约抛出 InvalidOperationException

    我有一个使用插件的应用程序 我在另一个应用程序域中加载插件 我使用 RemoteHandle 类http www pocketsilicon com post Things That Make My Life Hell Part 1 App
  • 如何在 .NET Framework 2.0 中模拟“Func<(Of <(TResult>)>) 委托”?

    我尝试使用这个类代码项目文章 http www codeproject com KB threads AsyncVar aspx在 VB NET 和 NET Framework 2 0 中 除了这一行之外 所有内容似乎都可以编译Privat
  • 为什么 int8_t 和用户通过 cin 输入显示奇怪的结果[重复]

    这个问题在这里已经有答案了 一小段代码让我发疯 但希望你能阻止我跳出窗外 看这里 include
  • 在 DataView 的 RowFilter 中选择 DISTINCT

    我试图根据与另一个表的关系缩小 DataView 中的行范围 我使用的 RowFilter 如下 dv new DataView myDS myTable id IN SELECT DISTINCT parentID FROM myOthe
  • MVC 在布局代码之前执行视图代码并破坏我的脚本顺序

    我正在尝试将所有 javascript 包含内容移至页面底部 我正在将 MVC 与 Razor 一起使用 我编写了一个辅助方法来注册脚本 它按注册顺序保留脚本 并排除重复的内容 Html RegisterScript scripts som
  • 复制目录内容

    我想将目录 tmp1 的内容复制到另一个目录 tmp2 tmp1 可能包含文件和其他目录 我想使用C C 复制tmp1的内容 包括模式 如果 tmp1 包含目录树 我想递归复制它们 最简单的解决方案是什么 我找到了一个解决方案来打开目录并读
  • java.io.Serialized 在 C/C++ 中的等价物是什么?

    C C 的等价物是什么java io Serialized https docs oracle com javase 7 docs api java io Serializable html 有对序列化库的引用 用 C 序列化数据结构 ht
  • 在 C 中初始化变量

    我知道有时如果你不初始化int 如果打印整数 您将得到一个随机数 但将所有内容初始化为零似乎有点愚蠢 我问这个问题是因为我正在评论我的 C 项目 而且我对缩进非常直接 并且它可以完全编译 90 90 谢谢 Stackoverflow 但我想
  • 为什么调用非 const 成员函数而不是 const 成员函数?

    为了我的目的 我尝试包装一些类似于 Qt 共享数据指针的东西 经过测试 我发现当应该调用 const 函数时 会选择它的非 const 版本 我正在使用 C 0x 选项进行编译 这是一个最小的代码 struct Data int x con
  • 如何检测表单的任何控件的变化?

    如何检测 C 中表单的任何控件的更改 由于我在一个表单上有许多控件 并且如果表单中的任何控件值发生更改 我需要禁用按钮 我正在寻找一些内置函数 事件处理程序 属性 并且不想为此创建自定义函数 不 我不知道任何时候都会触发任何事件any控制表
  • 如何禁用 fread() 中的缓冲?

    我正在使用 fread 和 fwrite 读取和写入套接字 我相信这些函数用于缓冲输入和输出 有什么方法可以在仍然使用这些功能的同时禁用缓冲吗 Edit 我正在构建一个远程桌面应用程序 远程客户端似乎 落后于服务器 我不知道可能是什么原因
  • 为什么 std::strstream 被弃用?

    我最近发现std strstream已被弃用 取而代之的是std stringstream 我已经有一段时间没有使用它了 但它做了我当时需要做的事情 所以很惊讶听到它的弃用 我的问题是为什么做出这个决定 有什么好处std stringstr
  • 外键与独立关系 - Entity Framework 5 有改进吗?

    我读过了several http www ladislavmrnka com 2011 05 foreign key vs independent associations in ef 4 文章和问题 https stackoverflow
  • 使用管道时,如果子进程数量大于处理器数量,进程是否会被阻塞?

    当子进程数量很大时 我的程序停止运行 我不知道问题是什么 但我猜子进程在运行时以某种方式被阻止 下面是该程序的主要工作流程 void function int process num int i initial variables for
  • 不同类型指针之间的减法[重复]

    这个问题在这里已经有答案了 我试图找到两个变量之间的内存距离 具体来说 我需要找到 char 数组和 int 之间的距离 char data 5 int a 0 printf p n p n data 5 a long int distan
  • 如何部署“SQL Server Express + EF”应用程序

    这是我第一次部署使用 SQL Server Express 数据库的应用程序 我首先使用实体 框架模型来联系数据库 我使用 Install Shield 创建了一个安装向导来安装应用程序 这些是我在目标计算机中安装应用程序所执行的步骤 安装
  • Oracle Data Provider for .NET 不支持 Oracle 19.0.48.0.0

    我们刚刚升级到 Oracle 19c 19 3 0 所有应用程序都停止工作并出现以下错误消息 Oracle Data Provider for NET 不支持 Oracle 19 0 48 0 0 我将 Oracle ManagedData
  • 从列表中选择项目以求和

    我有一个包含数值的项目列表 我需要使用这些项目求和 我需要你的帮助来构建这样的算法 下面是一个用 C 编写的示例 描述了我的问题 int sum 21 List

随机推荐