每天进步一点点!
1.3 了解编译系统如何工作好处
1. 优化程序性能
1.编码选择:
了解机器代码和编译器对不同C语句的转换方式,可以帮助程序员做出更好的编码选择,如使用switch语句还是if-else语句、循环结构的选择等。
2.函数调用开销:
了解函数调用的开销有助于优化程序性能,程序员可以考虑减少函数调用或使用内联函数等方式来提高效率。
3.循环和指针引用:
了解循环和指针引用的效率差异,可以帮助程序员选择更高效的代码实现方式。
4.局部变量 vs 引用参数:
将循环求和的结果放到局部变量中相比于放到通过引用传递的参数中,可以提高程序的运行速度。
5.算术表达式的重排:
通过简单地重新排列算术表达式中的括号,可以提高函数的运行速度。
2. 理解链接时出现的错误
1.链接错误:
在构建大型软件系统时,链接器操作常常导致一些令人困扰的程序错误。其中一个例子是链接器报告无法解析一个引用,这意味着什么?在第7章中,你将了解如何解决这类链接错误以及其他与链接相关的问题,比如静态变量和全局变量的区别、在不同的C文件中定义名字相同的两个全局变量会发生什么以及静态库和动态库的区别等。
2.命令行上排列库的顺序:
在命令行上排列库的顺序会对链接产生影响。具体来说,这段话提到了命令行上排列库的顺序对链接的影响。在第7章中,你将了解更多关于这方面的内容。
3.运行时链接错误:
有些链接错误直到运行时才会出现,这是其中最严重的一种情况。在第7章中,你将了解为什么会出现这种情况以及如何处理这类链接错误。
3. 避免安全漏洞
1.缓冲区溢出错误:
缓冲区溢出错误是造成大多数网络和Internet服务器上安全漏洞的主要原因之一。在第3章中,你将学习什么是缓冲区溢出错误以及为什么会出现这种错误。
2.程序栈:
了解数据和控制信息存储在程序栈上的方式对于理解缓冲区溢出错误非常重要。在第3章中,你将了解堆栈原理和程序栈的相关知识。
3.安全编程:
学习安全编程的第一步是理解数据和控制信息存储在程序栈上的方式会引起的后果。在第3章中,你将了解程序员、编译器和操作系统可以用来降低攻击威胁的方法。
1.4 处理器读并解释储存在内存中的命令
1.4.0 Unix系统上运行可执行文件的过程
编译和翻译:
源程序hello.c通过编译系统被翻译成可执行目标文件hello,并被存放在磁盘上。
输入到shell中:
为了在Unix系统上运行可执行文件,我们需要将其文件名输入到称为shell的应用程序中。在这个例子中,输入命令"./hello"表示要运行名为hello的可执行文件。
shell的作用:
shell是一个命令行解释器,它输出一个提示符并等待输入命令行。如果命令行的第一个单词不是内置的shell命令,那么shell会假设它是一个可执行文件的名字,并加载并运行该文件。
运行可执行文件:
在这个例子中,shell会加载并运行hello程序。hello程序会在屏幕上输出它的消息,然后终止执行。
提示符和下一命令行:
shell在程序执行完毕后会输出一个提示符,等待输入下一个命令行。
1.4.1 系统的硬件组成
1. 总线
总线是计算机系统中的组电子管道。
总线贯穿整个系统,负责在各个部件间传递信息字节。
总线承载着数据、地址和控制信号的传输。
总线传输的基本单位是字(word)。
字是定长的字节块,不同系统可能有不同的字长。
常见的字长有4个字节(32位)和8个字节(64位)。
字长是系统的基本参数。
不同系统的字长可以不同,没有固定的标准。
本书中并未做出固定的字长假设,根据需要在特定上下文中明确定义一个“字”的大小。
字长的确定与系统的设计和性能有关。
较长的字长可以提供更大的内存地址空间和更高的数据处理能力。
选择合适的字长需要考虑系统需求、硬件成本和性能因素。
2. I/O设备
I/O设备是系统与外部世界的联系通道。
I/O设备包括输入设备和输出设备,用于人机交互和数据存储。
示例系统中包括键盘、鼠标、显示器和磁盘驱动器。
每个I/O设备通过控制器或适配器与I/O总线相连。
控制器是I/O设备本身或主板上的芯片组。
适配器是插在主板插槽上的卡。
它们的功能都是在I/O总线和I/O设备之间传递信息。
控制器和适配器的区别在于封装方式。
控制器封装在I/O设备本身或主板上,通常是芯片组的形式。
适配器是插在主板插槽上的卡片形式。
第6章将详细说明磁盘等I/O设备的工作原理。
磁盘是一种长期存储数据和程序的设备,其工作原理需要深入了解。
第10章将介绍如何利用Unix I/O接口访问设备。
Unix I/O接口是应用程序与设备之间的通信接口。
重点关注网络类设备,但技术对其他设备也适用
3. 主存
主存是一个临时存储设备,用于存放程序和程序处理的数据。
物理上由动态随机存取存储器(DRAM)芯片组成。
逻辑上是线性的字节数组,每个字节有唯一地址(数组索引),从零开始。
程序中的机器指令由不同数量的字节构成,数据项的大小根据类型变化。
在Linux的x86-64机器上,不同类型的数据占用不同字节数。
第6章将介绍存储器技术,包括DRAM芯片的工作原理和主存的组成方式。
将具体探讨DRAM芯片的工作原理以及它们如何组合构成主存。
4. 处理器
中央处理单元(CPU)是执行存储在主存中的指令的引擎。
处理器的核心是一个叫做程序计数器(PC)的寄存器,它指向主存中的某个机器语言指令。
处理器按照一条非常简单的指令执行模型操作,模型由指令集架构决定。
处理器从程序计数器指向的内存位置读取指令,解释指令中的位,执行操作,并更新程序计数器指向下一条指令。
简单操作围绕着主存、寄存器文件和算术/逻辑单元(ALU)进行。
寄存器文件是一个小的存储设备,由多个单字长的寄存器组成,每个寄存器都有唯一的名字。
ALU用于计算新的数据和地址值。
1.4.2 运行hello程序
Shell程序等待命令输入:
Shell程序执行自己的指令,并等待用户输入命令。
输入命令:
当用户在键盘上输入字符串". /hello"时,Shell程序逐个字符读取并将其存储到内存中。
执行加载指令:
当用户按下回车键后,Shell程序知道命令输入结束。然后,Shell程序执行一系列的指令来加载可执行的hello文件,将文件中的代码和数据从磁盘复制到主存。
利用DMA技术直接存取数据:
通过直接存储器存取(DMA)技术,数据可以绕过处理器,直接从磁盘传输到主存。这样可以加快数据传输速度。
执行hello程序:
一旦目标文件hello中的代码和数据加载到主存,处理器开始执行hello程序的main函数中的机器语言指令。
数据显示:
在执行过程中,指令将"hello, world\n"字符串的字节从主存复制到寄存器文件,然后从寄存器文件复制到显示设备,最终显示在屏幕上。