| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> C++知识库 -> CSAPP 第一章 计算机系统漫游 -> 正文阅读 |
|
[C++知识库]CSAPP 第一章 计算机系统漫游 |
(前言???本书的目的是让我们了解系统执行hello程序时,系统发生了什么以及为什么这样。我们通过跟踪hello程序的生命周期来开始对系统的学习。从它被创建开始,到在系统上运行,输出简单的消息,然后终止。我们将沿着这个程序的生命周期,简要地学习一些逐步出现的关键概念、专业术语和组成部分,后面的章节将围绕这些内容展开。) 1、编译系统对.c文件hello.c编译产生可执行文件的命令 linux > gcc -o hello hello.c 这个过程虽然是通过一条命令完成的,然而实际上编译系统的处理过程却是非常复杂的,大致可以分为四个阶段,分别为预处理、 编译、 汇编、链接。 ? 预处理阶段:预处理器(cpp)根据以字符#开头的命令,修改原始的C程序。比如hello.c中第1行的#include <stdio.h> 命令告诉预处理器读取系统头文件stdio.h的内容,并把它直接插人程序文本中。结果就得到了另一个C程序,通常是以.i作为文件扩展名。? 编译阶段:编译器(cc1)将文本文件hello.i翻译成文本文件hello.s,它包含一个汇编语言程序,该程序包含函数main的定义,如下所示:
汇编阶段:接下来,汇编器(as)将hello.s翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序(relocatable object program) 的格式,并将结果保存在目标文件hello.o中。hello.o文件是一个二进制文件,它包含的17个字节是函数main的指令编码。如果我们在文本编辑器中打开hello.o文件,将看到一堆乱码。 链接阶段:请注意,hello程序调用了printf函数,它是每个C编译器都提供的标准C库中的一个函数。printf函数存在于一个名为printf.o的单独的预编译好了的目标文件中,而这个文件必须以某种方式合并到我们的hello.o程序中。链接器(Id)就负责处理这种合并。结果就得到hello文件,它是一个可执行目标文件(或者简称为可执行文件),可以被加载到内存中,由系统执行。 对于hello.c简单的程序,可以依靠编译系统生成正确有效的机器代码。但是,有一些重要的原因促使程序员必须知道编译系统是如何工作的。 a、程序优化 为了使程序运行更高效、更省存储空间,我们需要了解不同c语言转换为机器码的方式。比如,一个switch语句是否总是比一系列的if-else语句高效?一个函数调用的开销有多大?while循环比for循环更有效吗?指针引用比数组索引更有效吗?为什么将循环求和的结果放到一个本地变量中,会比将其放到一个通过引用传递过来的参数中,运行起来快很多呢?为什么我们只是简单地重新排列一下算术表达式中的括号就能让函数运行得更快?(在第3章中,我们将学习x86- 64,最近几代Linux、Macintosh 和Windows计算机的机器语言。我们学习编译器是怎样把不同的C语言结构翻译成这种机器语言。在第5章中,学习如何通过简单转换C语言代码,帮助编译器更好地完成工作,从而调整C程序的性能。在第6章中,将学习存储器系统的层次结构特性,C语言编译器如何将数组存放在内存中,以及C程序又是如何能够利用这些知识从而更高效地运行。) b、理解链接出现错误(在第7章将会学习) 比如,链接器报告说它无法解析一个引用,这是什么意思?静态变量和全局变量的区别是什么?如果你在不同的C文件中定义了名字相同的两个全局变量会发生什么?静态库和动态库的区别是什么?我们在命令行上排列库的顺序有什么影响?最严重的是,为什么有些链接错误直到运行时才会出现? c、避免安全漏洞 缓冲区溢出错误是造成大多数网络和Internet服务器上安安全漏洞的主要原因。学习安全编程的第一步就是理解数据和控制信息存储在程序栈上的方式会引起的后果。作为学习汇编语言的一部分,我们将在第3章中学习堆栈原理和缓冲区溢出错误。 2、硬件系统组成总线:贯穿整个系统的电子管道,携带信息字节并负责在各个部件间传递。通常总线被设计成传送定长的字节块,也就是字(word)。字中的字节数(即字长)是一个基本的系统参数,各个系统中都不尽相同。现在的大多数机器字长要么是4个字节(32位),要么是8个字节(64位)。本书中,我们不对字长做任何固定的假设。 I/O(输入/输出)设备:是系统与外部世界的联系通道。示例系统包括四个I/O设备:作为用户输人的键盘和鼠标,作为用户输出的显示器,以及用于长期存储数据和程序的磁盘驱动器(简单地说就是磁盘)。最开始,可执行程序hello就存放在磁盘上。每个I/O设备都通过一个控制器或适配器与1/O总线相连。控制器和适配器之间的区别主要在于它们的封装方式。控制器是I/O设备本身或者系统的主印制电路板(通常称作主板)上的芯片组。而适配器则是一块插在主板插槽上的卡。无论如何,它们的功能都是在I/O总线和I/O设备之间传递信息。 主存:是一个临时存储设备,在处理器执行程序时,用来存放程序和程序处理的数据。从物理上来说,主存是由一组动态随机存取存储器(DRAM)芯片组成的。从逻辑上来说,存储器是一个线性的字节数组,每个字节都有其唯一的地址(数组索引),这些地址是从零开始的。一般来说,组成程序的每条机器指令都由不同数量的字节构成。与C程序变量相对应的数据项的大小是根据类型变化的。比如,在运行Linux的x86-64机器上,short类型的数据需要2个字节,int和float类型需要4个字节,而long和double类型需要8个字节。(第6章将具体介绍存储器技术,比如DRAM芯片是如何工作的,它们又是如何组合起来构成主存的。) 处理器:中央处理单元(CPU),简称处理器,是解释(或执行)存储在主存中指令的引擎。处理器的核心是一个大小为一个字的存储设备(或寄存器),称为程序计数器(PC)。在任何时刻,PC都指向主存中的某条机器语言指令(即含有该条指令的地址)。从系统通电开始,直到系统断电,处理器一直在不断地执行程序计数器指向的指令,再更新程序计数器,使其指向下一条指令。处理器看上去是按照一个非常简单的指令执行:模型来操作的,这个模型是由指令集架构决定的。在这个模型中,指令按照严格的顺序执行,而执行一条指令包含执行一系列的步骤。处理器从程序计数器指向的内存处读取指令,解释指令中的位,执行该指令指示的简单操作,然后更新PC,使其指向下一条指令,而这条指令并不一定和在内存中刚刚执行的指令相邻(比如跳转)。 3、高速缓存 存放处理器近期可能会需要的信息。通过高速缓存,系统可以获得一个很大的存储器,同时访问速度也很快,原因是利用了高速缓存的局部性原理,即程序具有访问局部区域里的数据和代码的趋势。通过让高速缓存里存放可能经常访问的数据,大部分的内存操作都能在快速的高速缓存中完成。 存储结构金字塔 ?存储器层次结构的主要思想是上一层的存储器作为低一层存储器的高速缓存。因此,寄存器文件就是L1的高速缓存,L1是L2的高速缓存,L2是L3的高速缓存,L3是主存的高速缓存,而主存又是磁盘的高速缓存。在某些具有分布式文件系统的网络系统中,本地磁盘就是存储在其他系统中磁盘上的数据的高速缓存。 4、操作系统管理硬件 我们写的程序不会直接访问主存、磁盘、显示器等,而是依靠操作系统提供的服务。如下图: 操作系统有两个基本功能: (1)防止硬件被失控的应用程序滥用;(2)向应用程序提供简单一致的机制来控制复杂而又通常大不相同的低级硬件设备。操作系统通过几个基本的抽象概念(进程、虚拟内存和文件)来实现这两个功能。如下图: ?文件是对I/O设备的抽象表示;虚拟内存是对主存和磁盘的抽象表示;进程则是对处理器、主存和I/O设备的抽象表示。计算机系统的整个抽象如下: 5、进程与线程 同一个进程内部,有多个任务并发的需求 ---> 线程(共享空间、不共享计算) 进程是静态的:程序进入内存,分配对应资源、内存空间,同时产生一个主线程 线程是动态的:是可执行的计算单元(任务) 线程切换:需要保存上下文、保存现场 一个程序,读入内存全是0和1,从内存到CPU计算需要通过总线(如何区分一段0/1是数据还是指令,通过总线类别:控制线、数据线、地址线) 问题:是不是线程数量越多执行效率越高? 不是 线程太多,很多时间都浪费在切换线程上 问题:单核CPU多线程执行有没有意义? ?有 所有的处理器配置分类: ?多核CPU与缓存的组织结构: 缓存行(一次性读取的数据块):访问某个数据,先从L1找,找不到去L2找,再去L3找,如果都没有,从内存读过来放L3、L2、L1。实际中,一般一次性按照一块数据访问,叫做缓存行,依据的是空间局部性原理。(另一个程序的局部性原理叫时间局部性原理)。缓存行不能太大也不能太小(大太,访问命中率高,但是每次读取一块到L1效率低,太小则相反),目前实际中多为64字节。 超线程: 有时称为同时多线程,是一项允许一个CPU执行多个控制流的技术。它涉及CPU某些硬件有多个备份,比如程序计数器和寄存器文件,而其他的硬件部分只有二份,比如执行浮点算术运算的单元。常规的处理器需要大约20000个时钟周期做不同线程间的转换,而超线程的处理器可以在单个周期的基础上决定要执行哪一个线程。这使得CPU能够更好地利用它的处理资源。比如,假设一个线程必须等到某些数据被装载到高速缓存中,那CPU就可以继续去执行另一个线程。举例来说,Intel Core i7处理器可以让每个核执行两个线程,所以一个4核的系统实际上可以并行地执行8个线程。 6、虚拟内存虚拟内存是一个抽象概念,它为每个进程提供了一个假象,虚拟内存的运作需要硬件和操作系统软件之间精密复杂的交互,包括对处理器生成的每个地址的硬件翻译。基本思想是把一个进程虚拟内存的内容存储在磁盘上,然后用主存作为磁盘的高速缓存。? 程序代码和数据:对所有的进程来说,代码是从同一固定地址开始,紧接着的是和C全局变量相对应的数据位置。代码和数据区是直接按照可执行目标文件的内容初始化的。 堆:代码和数据区后紧随着的是运行时堆。代码和数据区在进程一开始运行时就被指定了大小,与此不同,当调用像malloc和free 这样的C标准库函数时,堆可以在运行时动态地扩展和收缩。 共享库:大约在地址空间的中间部分是一块用来存放像C标准库和数学库这样的共享库的代码和数据的区域。 栈:位于用户虚拟地址空间顶部的是用户栈,编译器用它来实现函数调用。和堆一样,用户栈在程序执行期间可以动态地扩展和收缩。特别地,每次我们调用一个函数时,栈就会增长;从一个函数返回时,栈就会收缩。 内核虚拟内存:地址空间顶部的区域是为内核保留的。不允许应用程序读写这个区域的内容或者直接调用内核代码定义的函数。相反,它们必须调用内核来执行这些操作。 (最后的话:本人水平有限,难有不足之处,欢迎点评,后续会继续更新CSAPP系列,同时完善相关知识点,比如进程与线程、虚拟内存等等) |
|
C++知识库 最新文章 |
【C++】友元、嵌套类、异常、RTTI、类型转换 |
通讯录的思路与实现(C语言) |
C++PrimerPlus 第七章 函数-C++的编程模块( |
Problem C: 算法9-9~9-12:平衡二叉树的基本 |
MSVC C++ UTF-8编程 |
C++进阶 多态原理 |
简单string类c++实现 |
我的年度总结 |
【C语言】以深厚地基筑伟岸高楼-基础篇(六 |
c语言常见错误合集 |
|
上一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/24 12:34:28- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |