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

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(使用前将#替换为@)

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

  • 金特 + XNA (C#)

    是否可以使用jint http jint codeplex com操作使用 XNA C 创建的 3D 环境 并向该环境添加功能 再次使用 jint 作为 Jint 的贡献者 我会推荐你Jint http jint codeplex com
  • 叮当错误?命名空间模板类的朋友

    以下代码在 clang 下无法编译 但在 gcc 和 VS 下可以编译 template
  • 并行化斐波那契序列生成器

    我正在学习并行化 在一项练习中 我得到了一些我应该提高性能的算法 其中之一是斐波那契数列生成器 array 0 0 array 1 1 for q 2 q lt MAX q array q array q 1 array q 2 我怀疑 这
  • 进程退出后 POSIX 名称信号量不会释放

    我正在尝试使用 POSIX 命名信号量进行跨进程同步 我注意到进程死亡或退出后 信号量仍然被系统打开 在进程 打开它 死亡或退出后是否有办法使其关闭 释放 早期的讨论在这里 当将信号量递减至零的进程崩溃时 如何恢复信号量 https sta
  • 如果 JSON.NET 中的值为 null 或空格,则防止序列化

    我有一个对象需要以这样的方式序列化 即 null 和 空白 空或只是空格 值都不会序列化 我不控制对象本身 因此无法设置属性 但我知道所有属性都是字符串 环境NullValueHandling显然 忽略 只能让我找到解决方案的一部分 它 似
  • 将 OpenCV Mat 转换为数组(可能是 NSArray)

    我的 C C 技能很生疏 OpenCV 的文档也相当晦涩难懂 有没有办法获得cv Mat data属性转换为数组 NSArray 我想将其序列化为 JSON 我知道我可以使用 FileStorage 实用程序转换为 YAML XML 但这不
  • 如何以编程方式播放 16 位 pcm 数组 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我有一个包含 16 位 pcm 值的短 数组 我希望能够在不添加任何标题 也不将任何文件保存到内存的情况下播放它 我知道我可能需要一个提供
  • 为什么需要数字后缀?

    C 语言 我确信还有其他语言 需要在数字文字末尾添加后缀 这些后缀指示文字的类型 例如 5m是一个小数 5f是一个浮点数 我的问题是 这些后缀真的有必要吗 或者是否可以从上下文中推断出文字的类型 例如 代码decimal d 5 0应该推断
  • C# 编译器数字文字

    有谁知道 C 编译器数字文字修饰符的完整列表 默认情况下 声明 0 使其成为 Int32 声明 0 0 使其成为 Double 我可以在末尾使用文字修饰符 f 来确保某些内容被视为 Single 例如像这样 var x 0 x is Int
  • 有什么方法可以重载 C# 中的扩展方法吗?

    我有以下模型模式 public abstract class PARENTCLASS public class CHILD A CLASS PARENTCLASS public static class EXTENSION public s
  • 如何在win32中使用GetSaveFileName保存文件?

    我编写此代码是为了获取 fileName 来保存我的文件 include stdafx h include
  • 使用 C# 中的 Google 地图 API 和 SSIS 包获取行驶距离

    更新 找到了谷歌距离矩阵并尝试相应地修改我的代码 我在这里收到无效参数错误 return new GeoLocation dstnc uri ToString catch return new GeoLocation 0 0 https 基
  • 用于连接 DataTable 上的动态列的动态 LINQ

    我目前遇到的情况不确定如何继续 我有两个从数据库填充的数据表 我还有一个可用的列名称列表 可用于将这两个数据表连接在一起 我希望编写一组 LINQ 查询 这些查询将 显示两个数据表中的行 内部联接 用于从一个数据表更新另一个数据表 显示一个
  • 时间:2019-03-17 标签:c++fstream并发访问

    如果从不同的进程 线程同时访问文件会发生什么 据我所知 没有锁定文件的标准方法 只有操作系统特定的功能 就我而言 文件将被经常读取而很少写入 现在如果A打开一个文件进行读取 ifstream 并开始读取块 和B打开相同的文件进行写入 ofs
  • Resharper:IEnumerable 的可能多重枚举

    我正在使用新的 Resharper 版本 6 在我的代码中的几个地方 它给一些文本加了下划线 并警告我可能存在IEnumerable 可能的多重枚举 我理解这意味着什么 并在适当的情况下采纳了建议 但在某些情况下 我不确定这实际上是一个大问
  • ALTER TABLE ... ADD CONSTRAINT 失败时将事务回滚到保存点

    有没有办法在事务中添加检查约束and如果失败回滚到以前的保存点 而不是回滚整个事务 就我而言 当 ALTER TABLE ADD CONSTRAINT 命令失败时 事务无法回滚到保存点 尝试这样做会引发 InvalidOperationEx
  • 使用 xslt 将 xml 转换为 xsl-fo 时动态创建超链接?

    我想使用 xsl 文件在 PDF 报告中创建标题 如果源文件包含超链接 则应将其呈现为超链接 否则呈现为纯文本 例如 我的 xml 如下所示 a href http google com target blank This is the h
  • 如何将 int 作为“void *”传递给线程启动函数?

    我最初有一个用于斐波那契变量数组的全局变量 但发现这是不允许的 我需要进行基本的多线程处理并处理竞争条件 但我无法在 pthread 创建中将 int 作为 void 参数提供 我尝试过使用常量指针 但没有成功 由于某些奇怪的原因 void
  • printf或iostream如何指定点后的最大位数

    字符串采用什么格式printf or iomanip我应该使用 iostream 中的运算符以以下格式打印浮点数 125 0 gt 125 125 1 gt 125 1 125 12312 gt 125 12 1 12345 gt 1 12
  • 使用 C# 动态创建按钮并按预定义的顺序放置它们

    NET 4 5 C 创建 Windows 窗体 我想动态创建和添加按钮并为其分配单击事件 但希望它们以特定的方式动态放置 就像图像一样 我的问题是如何以上述方式动态放置按钮 即 4x4 格式 一行 4 个按钮 4 列 但行数不受限制 是否可

随机推荐