导图社区 程序是怎样跑起来的
本书从计算机的内部结构开始讲起,以图配文的形式详细讲解了二进制、内存、数据压缩、源文件和可执行文件、操作系统和应用程序的关系、汇编语言、硬件控制方法等内容,目的是让读者了解从用户双击程序图标到程序开始运行之间到底发生了什么。同时专设了“如果是你,你会怎样介绍?”专栏,以小学生、老奶奶为对象讲解程序的运行原理,颇为有趣。本书图文并茂,通俗易懂,非常适合计算机爱好者及相关从业人员阅读。
编辑于2022-03-31 09:28:29植物生殖生理,概述了植物生殖生理的复杂过程,从基本概念到具体机制,再到实际应用,做了全面而详细的阐述。描述了花朵在特定生长阶段的状态。探讨了影响花粉生活力的外界条件,如湿度、温度、CO2和O2的相对浓度等。描述了花粉萌发、花粉和柱头的相互识别,以及受精过程中雌蕊的生理变化。
C语言知识点整理,内容涵盖了C语言学习的多个重要方面,从简单的程序结构开始,逐步深入到数据类型、运算符、流程控制结构等核心概念。详细列出了C语言中的运算符,包括自加自减运算符、优先级和结合性等关键概念,帮助学习者理解和掌握这些运算符的使用方法。涵盖了“循环”、“表达式”、“流程结构”、“语句”、“选择判断”和“goto”等知识点,这些都是编写复杂程序时必须掌握的技能。还涉及了函数、数组、内存分区、多文件编程、内存管理、位运算、类型转换、文件操作、类型修饰符、预处理和其他高级主题,如类型重命名等。
计算机操作系统思维导图总结,内容包含操作系统引论、进程的描述与控制、处理机调度与死锁、存储器管理、虚拟存储器、输入输出系统、文件管理。
社区模板帮助中心,点此进入>>
植物生殖生理,概述了植物生殖生理的复杂过程,从基本概念到具体机制,再到实际应用,做了全面而详细的阐述。描述了花朵在特定生长阶段的状态。探讨了影响花粉生活力的外界条件,如湿度、温度、CO2和O2的相对浓度等。描述了花粉萌发、花粉和柱头的相互识别,以及受精过程中雌蕊的生理变化。
C语言知识点整理,内容涵盖了C语言学习的多个重要方面,从简单的程序结构开始,逐步深入到数据类型、运算符、流程控制结构等核心概念。详细列出了C语言中的运算符,包括自加自减运算符、优先级和结合性等关键概念,帮助学习者理解和掌握这些运算符的使用方法。涵盖了“循环”、“表达式”、“流程结构”、“语句”、“选择判断”和“goto”等知识点,这些都是编写复杂程序时必须掌握的技能。还涉及了函数、数组、内存分区、多文件编程、内存管理、位运算、类型转换、文件操作、类型修饰符、预处理和其他高级主题,如类型重命名等。
计算机操作系统思维导图总结,内容包含操作系统引论、进程的描述与控制、处理机调度与死锁、存储器管理、虚拟存储器、输入输出系统、文件管理。
程序是怎样跑起来的
第一章 对程序员来说CPU是什么
基础知识
程序是指示计算机每一步动作的一组指令
程序是由数据和指令组成的
机器语言CPU可以直接识别和使用的语言,CPU能够直接识别和执行的只有机器语言
正在运行的程序存储在内存中
内存中,用来表示命令和数据存储位置的数值是内存地址
内存是保存命令和数据的场所,通过地址来标记和指定
计算机的构成元件中,负责程序的解释和运行的是CPU
编译是指将使用高级编程语言编写的程序转换成机器语言的过程,其中用于转换的程序被称为编译器
用户发出启动程序的指示后,Windows等操作系统会把硬盘中保存的程序复制到内存中
1.1 CPU的内部结构解析
CPU负责解释和运行最终转换成机器语言的程序内容
程序运行的一般流程
用编程语言编写程序
将程序编译后转换成机器语言的EXE文件
程序运行后,在内存中生成EXE文件的副本
CPU解释并执行程序
CPU组成(从功能来分)
寄存器
可用来暂存指令、数据等处理对象,可将其看作是内存的一种
控制器
负责把内存上的指令、数据等读入寄存器,并根据指令的执行结果控制整个计算机
运算器
负责运算从内存读入寄存器的数据
时钟
负责发出CPU开始计时的时钟信号
内存
计算机的主存储器,简称主存
主存通过芯片等与CPU相连,主要负责存储指令和数据
由可读写的元素构成,每个字节(1节=8位)都带有一个地址编号
CPU可以通过地址读取主存中的指令和数据,也可以写入数据
程序运行机制
程序启动后,根据时钟信号,控制器会从内存中读取指令和数据
通过对指令加以解释和运行,运算器就会对数据进行运算
控制器根据该运算结果来控制计算机(如磁盘会内存的输入输出)
1.2 CPU是寄存器的集合体
在CPU的四个构成部分中,我们只需了解寄存器,这是因为程序是把寄存器作为对象来描述的
寄存器中存储的内容既可以是指令,也可以是数据
在程序员眼中,CPU是具有各种功能的寄存器的集合体
使用高级编程语言编写的程序会在编译后转化成机器语言,然后在通过CPU内部的寄存器来处理
寄存器种类
累加寄存器
存储执行运算的数据
标志寄存器
存储运算处理后的CPU的状态
程序计数器
存储下一条指令所在内存的地址
基址寄存器
存储数据内存的起始地址
变址寄存器
存储基址寄存器的相对地址
通用寄存器
存储任意数据
指令寄存器
存储指令
CPU内部使用,程序员无法通过程序对该寄存器进行读写操作
栈寄存器
存储栈区域的起始地址
1.3 决定程序流程的程序计数器
Windows等操作系统把程序从硬盘复制到内存后,会把程序计数器设定为一个初值,然后程序便开始运行
CPU每执行一条指令,程序计数器的值就会自动增加与指令长度相应的值
CPU控制器就会按照程序计数器的值,从内存中读取命令并执行
程序计数器决定着程序的流程
1.4 条件分支和循环机制
条件分支和循环中使用的跳转指令,会参照当前执行的运算结果来判断是否跳转
CPU在进行运算时,标志寄存器的数值会根据运算结果自动设定
条件分支在跳转指令前会进行比较运算。至于是否执行跳转指令,则由CPU在参考标志寄存器的数值后进行判断
1.5 函数的调用机制
函数调用处理是通过把程序计数器的值设定成函数的存储地址来实现的
和条件分支、循环的机制有所不同,单纯的跳转指令无法实现函数的调用
函数的调用需要在完成函数内部处理后,处理流程再返回到函数调用点(函数调用指令的下一个地址)
函数调用
函数调用使用的是机器语言的call指令,并非跳转指令
在将函数的入口地址设定到程序计数器之前,call指令会把调用函数后要执行的指令地址存储在名为栈的主存内
待函数处理完毕后,再通过函数的出口来执行return指令
return命令的功能是把保存在栈中的地址设定到程序计数器中
在编译高级编程语言的程序后,函数调用的处理会转换成call指令,函数结束的处理会转换成return指令,这样程序的运行就很流畅了
1.6 通过地址和索引实现数组
数组是指多个同样数据类型的数据在内存中连续排列的形式
作为数据元素的各个数据会通过连续的编号被区分开来,这个编号被称为索引(index)
指定索引后,就可以对该索引所对应地址的内存进行读写操作
索引和内存地址的变换工作由编译器自动实现(CPU是通过基址寄存器和变址寄存器来指定内存地址的)
CPU会把基址寄存器+变址寄存器的值解释称实际查看的内存地址
变址寄存器的值就相当于高级编程语言程序中数组的索引功能
1.7 CPU的处理其实很简单
虽然高级编程语言编写的程序相当复杂,但是CPU实际处理的事情很简单
call/retain指令:函数的调用/返回调用前的地址
跳转指令:实现条件分支、循环、强制跳转等
运算指令:用累加寄存器执行算数运算、逻辑运算、比较运算和移位运算
数据转送指令:寄存器和内存、内存和内存、寄存器和外围设备之间的数据读写操作
第二章 数据是用二进制表示的
基础知识
位是最小单位,字节是基本单位。
内存和磁盘都使用字节单位来存储和读写数据,使用位单位则无法读写数据。因此字节是信息最基本的单位
数字、字符串和图像等信息在计算机内部都是以二进制数值形式来表现的
2.1 用二进制表示计算机信息的原因
计算机是由IC构成的,IC的所有引脚,只有直流电压0V和5V两个状态
IC的这个特性,决定了计算机的信息数据只能用二进制数来表示
计算机处理信息的最小单位--位,就相当于二进制中的一位
在程序中,即使是十进制数和文字等记述信息,在编译后也会转换成二进制数值,所以程序运行时计算机内部处理也是用二进制表示的信息
用二进制表示的信息,计算机不区分它是数值、文字还是图片,而是根据程序员发出的指令来进行信息的处理
2.2 什么是二进制数
二进制数的值转化成十进制数的值,只需要将二进制数的各位的值和位权相乘,然后将相乘的结果相加即可
2.3 移位运算和乘除运算的关系
移位运算指的是将二进制数的值的各个位依次向左或向右移动的运算
<<左移
右侧空出来的位直接补0,若移位操作使最高位或最低位溢出,溢出部分直接丢弃
在不存在溢出的情况下,每左移一位,二进制数值相应增加1倍
>>(算术)右移
当二进制数作为带符号的数值进行运算时,移位后要在最高位填充移位前符号位的值(0或1)
若数值是补数表示的负数值,那么右移后空出来的最高位补1
2.4 便于计算机处理的“补数”
二进制数中表示负数时,一般会把最高位数作为符号使用
符号位是0时表示正数,符号位是1时表示负数
计算机在做减法运算时,实际上内部是在做加法运算
用加法运算来实现减法运算,在表示负数时就要使用“二进制的补数”
补数就是用正数来表示负数
求补数方法
将二进制数的各位数的数值全部取反,然后再将结果加1
实例(以8位二进制数表示-1)
只需求得1,即00000001的补数
将各数位的0取反,再将取反的结果加1,最后得到11111111
2.5 逻辑右移和算术右移动的区别
逻辑右移
当二进制数的值表示图形模式而非数值时,移动位后需要在最高位补0
算术右移
当二进制数作为带符号的数值进行运算时,移位后要在最高位填充移位前符号位的值(0或1)
若数值是补数表示的负数值,那么右移后空出来的最高位补1
第三章 计算机进行小数运算时出错的原因
基础知识
将小数分为基数、尾数、指数和符号4部分进行表现的形式浮点数
浮点数是指把小数用“符号 尾数*基数的指数次冪”这种形式来表示
1.1 把0.1累加100次得不到10
程序没错,计算机因为没错,但答案不对,为什么?答案在下面
1.2 用二进制数表示小数
实例(把二进制1011.0011转换为十进制)
将各数位的数值和位权相乘的结果相加即可
0次幂前的位权按照1次幂、2次幂的方式递增
0次幂后的位权按照-1次幂、-2次幂的方式递减
转换后十进制数是11.1875
1.3 计算机出错的原因
计算机之所以会出现运算错误,是因为有一些十进制的小数无法转换成二进制数
无法正确表示的数值,最后都变成了近似值
实例
十进制数0.1转化成二进制数后,会变成0.000 1100 1100……(1100循环)这样的循环小数
计算机在遇到循环小数时,会根据变量数据类型所对应的长度将数值四舍五入或者从中间截断
1.4 什么是浮点数
单精度浮点数32位
双精度浮点数64位
浮点数是指把小数用“符号 尾数*基数的指数次冪”这种形式来表示
因为计算机内部使用的是二进制数,所以基数自然就是2
浮点数内部构造
像0.12345x10^3和0.12345x10^(-1)这样使用与实际小数点位置不同的书写方法来表示小数的形式称为浮点数
与浮点数相对的是定点数,使用定点数表示小数点时,小数点的位置固定不变
1.5正则表达式和EXCESS系统
正则表达式:按照特定规则来表示数据的形式
十进制的浮点数应该遵循:小数点前面是0,小数点后面第1位不能是0
尾数部分使用正则表达式
在二进制中,使用将小数点前面固定为1的正则表达式
指数部分使用EXCESS系统
使用这种方法主要是为了表示负数时不使用符号位
1.7 如何避免计算机出错
计算机出错的原因之一是使用浮点数来处理小数(也有因位溢出而造成错误的情况)
避免出错
回避策略
把小数转换成整数
BCD(二进制化十进制)方法
第四章 熟练使用有棱有角的内存
基础知识
高级编程语言中的数据类型不仅表示存储在该内存区域的数据类型,还表示占据内存区域的大小
在32位内存地址的环境中,指针变量的长度是32位
指针指的是用来存储内存地址的变量,因此在32位内存地址的环境中必有32位
1.1 内存的物理机制很简单
内存实际上是一种名为内存IC的电子元件
内存IC主要分为:
ROM(Read Only Memory)只能用来读取的内存
RAM(Random Access Memory)可被读取和写入的内存
需要经常刷新以保存数据的DRAM(Dynamic RAM)
不需要刷新电路即能保存数据的SRAM(Static RAM)
内存IC中有电源引脚、地址信号引脚、数据信号引脚、控制信号引脚等IC引脚、通过为其指定地址(address),来进行数据的读写
向内存内写入数据
接通电源—>用地址引脚确定存储场所—>由数据引脚输入数据—>用WR或RD引脚决定写入或者读出
WR和RD这样可以控制IC运行的信号称为控制信号,其中WR和RD同时为0时,写入和读出的操作都无法运行
4.2 内存的逻辑模型是楼房
内存可以把它假想成每层都存储着数据的楼房
数据类型表示存储的是何种类型的数据,从内存来看,就是占用的内存大小(占有的楼层数)的意思
物理上以1个字节为单位来逐一读写数据的内存,在程序中,通过指定其类型(变量的数据类型等),也能实现以特定字节数为单位进行读写
4.3 简单的指针
指针也是一种变量,它所表示的不是数据的值,而是存储着数据的内存的地址
通过使用指针,就可以对任意指定地址的数据进行读写
定义指针时前面加数据类型,表示从指针存储的地址中一次能够读写的数据字节数
4.4 数组是高效使用内存的基础
数组是指多个同样数据类型的数据在内存中连续排列的形式
作为数据元素的各个数据会通过连续的编号被区分开来,这个编号被称为索引
指定索引后,就可以对该索引所对应地址的内存进行读写操作
索引和内存地址的变换工作由编译器自动实现(CPU是通过基址寄存器和变址寄存器来指定内存地址的)
之所以说数组是内存的使用方法的基础,是因为数组和内存的物理构造完全一样,特别是1字节类型的数组,它和内存的物理构造完全一致
4.5 栈、队列以及环形缓冲区
栈和队列,都可以不通过指定地址和索引来对数组的元素进行读写
为了实现临时保存数据的目的,使用栈的机制非常方便。当我们需要暂时舍弃当前的数据,随后再原貌还原时,会使用栈
栈用的是LIFO(Last Input First Out,后入先出)方式,而队列用的则是FIFO(First Input First Out,先入先出)方式
当我们在内存中预留出栈和队列所需要的空间,并确定好写入和读出顺序后,就不用再指定地址和索引了
这里的栈不是第一章进行数据调用时使用的栈,而是程序员自身做成的LIFO形式的数据存储方式(该栈的实体是数组)
当我们需要处理通讯中发送的数据时,或由同时运行的多个程序所发送过来的数据时,会用到这种对队列中存储不规则数据进行处理的方法
队列一般是以环状缓冲区的方式来实现的
4.6 链表使元素的追加和删除更容易
链表的实现
数据域加指针域
链表的删除
元素删除以后,虽然在物理内存上存在着,但在逻辑上已经删除了
链表添加元素
4.7 二叉树查找使数据搜索更有效
二叉查找树是指在链表的基础上往数组中追加元素时,考虑到数据的大小关系,将其分成左右两个方向的表现形式,但实际的内存并不会分两个方向,这是程序在逻辑上实现的
使用二叉查找树时,当目标数据比现在读出来的数据小时就可以转到左侧,反之目标数据较大时即可转到链表的右侧,这样就加快了找到目标数据的速度
第五章 内存和磁盘的亲密关系
基础知识
存储程序的方式指的是在存储装置中保存程序,并逐一运行的方式。现代计算机采用的就是存储程序方式
通过使用内存来提高磁盘访问速度的机制称为Disk Cache(磁盘缓存)。磁盘缓存指的是,把从磁盘中读出的数据存储在内存中,当该数据再次被读取时,不是从磁盘而是从内存中直接高速读出。
把磁盘的一部分作为假想内存来使用的机制称为虚拟内存(virtual memory),借助虚拟内存哪怕是内存不足的计算机,也可以运行很大的程序
Windows中,在程序运行时,存储着可以动态加载调用的函数和数据的文件称为DLL(DLL文件),Dynamic Link Liabrary的简称
在EXE程序文件中,静态加载函数的的方式称为静态链接
在Windows计算机中,一般磁盘中1个扇区通常为512字节
扇区是磁盘保存数据的物理单位
内存和磁盘都具有存储数据命令和程序的功能
内存是利用电流来实现存储的
磁盘是利用磁效应实现存储的
内存高速高价
磁盘低速廉价
在C语言中,调用函数后,需要执行栈清理处理指令
栈清理处理是指:把不需要的数据从接收和传递函数的参数时使用的内存上的栈区域中清理储区
内指主要指的是主内存(负责存储CPU中运行的程序和数据的内存),磁盘主要是指硬盘
5.1 不读入内存就无法运行
存储程序方式:程序保存在存储设备中,通过有序地被读出来实现运行,这一机制称为存储程序方式
磁盘中存储的程序,必须要加载到内存后才能运行。在磁盘中保持的原始程序是无法直接运行的。
负责解析和运行程序内容的CPU,需要通过内部程序计数器来指定内存地址,然后读出程序
Windows双击打开一个程序文件就是把这个存储在硬盘上的文件加载到内存里面,然后看“任务管理器”内存消耗就变多了。结束某个程序,就是把加载到内存中的这个程序从内存中剔除
5.2 磁盘缓存加快了磁盘访问速度
磁盘缓存:把磁盘中读出的数据存储到内存空间中来,这样当下一次需要读取同一数据时,就不用通过实际的磁盘,而是从磁盘缓存中把内容读出
5.3 虚拟内存把磁盘作为部分内存来使用
虚拟内存:虚拟内存指的是把磁盘的一部分作为假想的内存来使用
虚拟内存虽说是把磁盘作为内存的一部分来使用,但实际上正在运行的程序部分,在这个时间点上必须存在在内存中
即为了实现虚拟内存,就必须把实际内存(也称物理内存)的内容,和磁盘上的虚拟内存的内容进行部分置换,并同时运行程序
5.4 节约内存的编程方法
通过DLL文件实现函数共有
DLL文件是程序运行时可以动态加载Library(函数和数据的集合)的文件
通过调用_stdcall来减小程序文件的大小
_stdcall是standard call 的简称
当程序调用函数后,需要执行栈清理处理指令,多次调同一个函数时,就要多次执行栈清理处理,极大的 浪费资源,而使用了_stdcall,就可在被调方执行栈处理,多次调用仅需执行一次栈清理处理,节省了空间
该命令是程序编译时由编译器自动附加到程序中的
栈清理处理是指把不需要的数据从接收和传递函数的参数时使用的内存上的栈区域中清理出去
5.5磁盘的物理结构
磁盘的物理结构是指:磁盘存储数据的形式
磁盘是通过把其物理表面划分成多个空间来使用的
扇区方式
Windows一般使用扇区方式
把磁盘分为若干个同心圆的空间就是磁道
把磁盘按照固定大小(能存储的数据长度相同)划分成的空间就是扇区
可变长方式
扇区是对磁盘进行物理读写的最小单位
在Window的逻辑方面对磁盘进行读写的单位是扇区的整数倍簇
以簇为单位进行读写时,1簇中没有填满的区域会保存不被使用的状态
不同的文件不能存储在同一个簇中,否则就会导致有一方的文件不能被删除
因此不管多小的文件,都会占用1簇的空间
第6章 亲自尝试压缩数据
基础知识
文件存储的基本单位是1字节
BMP(BITMAP)格式的图像文件,是没有压缩过,因此要比JPEG格式等压缩过的图像文件大不少
可逆压缩和非可逆压缩]的不同点
压缩后的数据能复原的是可逆压缩
无法复原的是非可逆压缩,像JPEG格式这样的图片,之所以压缩后会让人感到不自然,就是因为使用了非可逆压缩
RLE算法
文件内容用“数据的值*循环次数”来表示的压缩方法
6.1 文件以字节为单位保存
文件是将数据存储在磁盘等存储媒介中的一种形式
程序文件中存储数据的单位是字节
文件就是字节数据的集合
从物理上对磁盘进行读写时是以扇区的整数倍(簇)(如512字节)为单位的,但另一方面,程序则可以在逻辑上以字节为单位对文件的内容进行读写
在任何情况下,文件中的字节数据都是连续存储的
6.2 RLE(Run Length Encoding,行程长度编码)
算法机制
把文件内容用数据*重复次数 的形式来表示压缩方法称为RLE算法
算法缺点
不适合文本文件压缩
算法优点
对图像文件压缩效果显著
6.3 用二叉树实现哈夫曼编码
哈夫曼算法
为各压缩对象文件分别构造最佳的编码体系,并以该编码体系为基础来进行压缩
用什么样的编码(哈夫曼编码)对数据进行分割,就要由各个文件而定
哈夫曼算法的关键在于:多次出现的数据用小于8位的字节数来表示,不常用的数据则可以用超过8位的字节数来表示
用哈夫曼算法压缩过的文件中,存储着哈夫曼编码信息和压缩过的数据
哈夫曼树:在哈夫曼算法中,通过借助哈夫曼树构造编码体系,即使在不使用字符区分符号的情况下,也可以构建能够明确进行区分的编码体系
使用哈夫曼树后,出现频率越高的数据所占用的数据位就越少,而且数据的区分也可以很清晰的实现
利用哈夫曼算法能够大幅度提升压缩比率
6.4可逆压缩和非可逆压缩
可逆压缩:能还原到压缩前状态的压缩
非可逆压缩:无法还原到压缩前状态的压缩
Windows的标准图像数据形式为BMP,是完全未压缩的
由于与显示器以及打印机输出的bit(点)是可以直接映射的,因此便有了BMP(bitmap)这一名称
常用的图片格式还有:BMP、JPEG、TIFF、GIF格式等
对于图像来说通常使用非可逆压缩。因为对于图像来说,即使有时无法还原到压缩前那样鲜明的图像状态,只要肉眼看不出也没关系。例如JPEG格式使用的就是非可逆压缩
第7章 程序在何种环境中运行
基础知识
应用的运行环境指操作系统和计算机本身(硬件)的种类。
应用的运行环境通常使用类似于Windows(OS)和AT兼容机(硬件)这样的OS和硬件的种类来表示的
AT兼容机是指,可以和IBM开发的PC/AT在硬件上相互兼容的计算机的总称。称为“PC/AT兼容机”。
Windows上的应用,在MacOS上无法运行,应用是为了在特定操作系统上运行而做成的
Java虚拟机的功能是运行Java应用的字节代码。只要分别为各个环境安装专用的Java虚拟机,同样的字节代码就能在各种环境下运行了
7.1 运行环境 = 操作系统+硬件
程序中包含着运行环境这一内容。操作系统和硬件决定了程序的运行环境
CPU只能解释本身固有的机器语言,不同的CPU能解释的机器语言的种类也是不同的
程序员用C语言等编写的程序,在编写阶段仅仅是文本文件。文本文件在任何环境下都能显示和编辑。我们称之为源代码
通过对源代码进行编译,就可以得到本地代码
机器语言的程序称为本地代码
7.2 Windows克服了CPU以外的硬件差异
计算机的硬件是不仅仅由CPU构成的,还包括用于存储程序指令和数据的内存,以及通过I/O连接的键盘、显示器、硬盘、打印机等外围设备
Wndows操作系统为克服这些硬件构成的差异作出了很大贡献
Windows之前,MS-DOS时代,不同机型的内存和I/O地址的构成等都是不同的,因此每个机型都需要专门的应用。因为这些应用软件中存在着直接操作计算机硬件的部分。这是因为一是当时MS-DOS的功能尚不完善,二是为了提高程序的运行速度
windows操作系统之下,同样的应用(本地代码)在任何机型上都可以运行
在Windows的应用软件中,键盘输入,显示器输出等并不是直接向硬件发送指令,而是通过向Windows发送指令来间接实现。因此,程序员就不用注意内存和I/O地址的不同构成了
7.3 不同操作系统的API不同
应用程序向操作系统传递指令的途径称为API
像键盘输入、鼠标输入、显示器显示等同外围设备进行输入输出操作的功能,都是通过API提供的
在同类型操作系统下,不管硬件如何,API基本上没有差别
7.4 利用虚拟机获得其他操作系统环境
利用虚拟机软件,可以不通过移植来运行其他操作系统的应用
7.5 提供相同运行环境的Java虚拟机
除虚拟机外,Java也能够提供不依赖于特定硬件及操作系统的程序运行环境
我们说的Java有两层意思
作为编程语言的Java
Java语言是将Java语法记述的源代码编译后运行,不过,编译后生成的不是特定CPU使用的本地代码,而是名为字节代码的程序
作为程序运行环境的Java
字节代码的运行环境就称为Java虚拟机
Java虚拟机是一边把Java字节代码逐一转换成本地代码一边运行的
实例
在使用用于AT兼容机的Java编译器和Java虚拟机的情况下,编译器会将程序员编写的源代码(sample.java)转换成字节代码(sample.class)
而Java虚拟机(Java.exe)则会把字节代码变换成AT兼容机的CPU使用的本地代码,然后由CPU负责实际的处理
在程序运行时,将编译后的字节代码转换成本地代码,这样的操作方法看上去有些迂回,但由此可以实现同样的字节代码在不同的环境下运行。
如果能够结合为各种类型的操作系统和硬件做成的Java虚拟机,那么,同样字节代码的应用就可以在任何环境下运行了
7.6 BIOS和引导程序
BIOS:基本输入输出(Basic Input/Output System)系统
BIOS存储在ROM中,是预先内置在计算机主机内部的程序
BIOS有对键盘、磁盘、显卡等的基本控制程序
BIOS有“引导程序”的功能,引导程序是存储在启动驱动器起始区域的小程序
开机后,BIOS会确认硬件是否正常运行,没有问题的话就会启动引导程序
引导程序的功能是把在硬盘等记录的OS加载到内存中运行
第八章 从源文件到可执行文件
基础知识
将多个目标文件结合生成EXE文件的工具称为链接接器,通过编译和链接,得到EXE文件
扩展名为.obj的目标文件的内容,是本地代码。通过对源文件进行编译,得到目标文件
把多个目标文件收录在一起的文件称为库文件,链接器会从库文件中抽取出必要的目标文件并将其结合到EXE文件中。此外,还存在一种程序运行时结合的DLL形式的库文件。
仅包含Windows的DLL文件中存储的函数信息的文件称为导入库,把导入库信息结合到EXE文件中,这样程序在运行时就可以利用DLL内的函数了
在程序运行时,用来动态申请分配(数据和对象)的内存区域形式称为堆,堆的内存空间会根据程序的命令进行申请及释放。
CPU可以解析和运行的程序形式称为本地代码(机器语言代码),通过编译源代码得到本地代码
编译器和解释器
编译器是在运行前对所有源代码进行解释处理的
解释器则是在运行时对源代码的内容一行一行地进行解释处理的
使用DLL文件的好处是
DLL文件中的函数可以被多个程序公用
借助该功能可以节约内存和磁盘
Build指的是什么?
根据开发工具种类的不同,有的编译器可以通过选择“Build”菜单来生成EXE文件
这种情况下,Build指的是连续执行编译和链接
垃圾回收机制
指对处理完毕后不再需要的堆内存空间的数据和对象进行清理,释放它们所使用的内存空间
进行该处理时,C语言使用free()函数,C++使用delete运算符
在C++基础上开发出来的Java及C#语言中,程序运行环境会自动进行垃圾回收机制
8.1 计算机只能运行本地代码
用某种语言编写的是源代码,保存源代码的文件是源文件
源代码无法直接运行,这是因为CPU能直接解析并运行的不是源代码而是本地代码的程序,CPU只能解释转换成本地代码的程序内容
使用不同编程语言编写的代码,转换成本地代码后,也就变成同一种语言(机器语言)来表示了
8.2 本地代码的内容
Windows中EXE文件的程序内容,使用的是本地代码
本地代码的内容是各种数值的罗列
8.4 仅靠编译是无法得到可执行文件的
编译器把源代码转换成本地文件后,此本地文件也是无法直接运行的,因为它还处于未完成状态。为了得到可执行文件,编译之后还需要进行“链接”处理
编译器对C源文件进行编译后生成扩展名为”.obj”的目标文件,虽然目标文件的内容是本地代码,但却无法直接运行,因为该程序还处于未完成状态
把多个目标文件结合起来,生成一个可执行文件的处理就是链接,运行链接的程序就称为链接器
8.5 启动及其库文件
库文件指的是把多个目标文件集成保存到一个文件中的形式。
链接器指定库文件后,就会从中把需要的目标文件抽取出来,并同其他目标文件结合生成可执行文件
8.6 DLL文件及导入库
Windows以函数的形式为应用提供了各种功能。这种形式的函数称为API
API的目标文件,并不是存储在通常的库文件中,而是存储在名为DLL(Dynamic Link Library)文件的特殊库文件中
DLL文件是程序运行时动态结合的文件
8.7 可执行文件运行时的必要条件
EXE文件是作为单独的文件存储在硬盘中的,双击EXE文件,就会把EXE文件的内容加载到内存中运行
本地代码在对程序中记述的变量进行读写时,是参照数据存储的内存地址来进行命令的。
在调用函数时,程序的处理流程就会跳转到存储着程序处理内容的内存地址上
可执行文件作为本地代码的程序,并没有指定变量以及函数的实际内存地址
在可制行文件中,变量和函数的内存地址的值,是如何来表示的
可执行文件中给变量以及函数分配了虚拟的内存地址
在程序运行时,虚拟的内存地址会转换成实际的内存地址
连接器会在可执行文件的开头,追加转换内存地址所需要的必要信息,这个信息称为再配置信息
可执行文件的再配置信息,就成为了变量和函数的相对地址
在源代码中,虽然变量以及函数是在不同位置分散记述的,但是在链接后的可执行文件中,变量及函数就会变成一个连续排列的组
各变量的内存地址就可以用相对于变量组起始位置这一基点的偏移量来表示,各函数的内存地址也可以用相对于函数组起始位置这一基点的偏移量来表示
各组基点的内存地址则是在程序运行时被分配的
8.8 程序加载时会生成堆和栈
可执行文件的内容分为再配置信息,变量组和函数组,当程序加载到内存后,除此之外还会额外生成两个组,那就是栈和堆
栈是用来存储函数内部临时使用的变量(局部变量),以及函数调用时所用的参数的内存区域
堆是用来存储程序运行时的任意数据及对象的内存领域
注意:EXE文件中并不存在堆和栈的组,堆和栈需要的内存空间是在EXE文件加载到内存后开始运行时得到分配的, 因而内存中的程序,就是由用于变量的内存空间,用于函数的内存空间,用于栈的内存空间和用于堆的内存空间构成
堆和栈
相同点
二者的内存空间都是在程序运行时得到申请分配的
不同点
栈中对数据进行存储和舍弃(清理处理)的代码,是由编译器自动生成的,因此不需要程序员的参与, 使用栈的数据的内存空间,每当函数被调用时都会得到申请分配,并在函数处理完毕后自动释放
堆的内存空间则要程序员编写程序,来明确进行申请分配或释放
C语言中是通过malloc()函数进行申请分配、通过free()函数来释放的
C++是通过new运算符来生气分配、通过delete运算符来释放的
内存泄露
若没有释放堆内存空间,那么在程序处理完毕后,该内存会一直存在,这种现象叫内存泄漏
第9章 操作系统和应用的关系
基础知识
监控程序的主要功能是程序的加载和运行。监控程序也可以说是操作系统的原型
在操作系统上运行的程序称为应用或应用程序
调用操作系统功能称为系统调用(system call)。应用通过系统调用(system call)间接控制硬件
9.1 操作系统功能的历史
通过事先启动监控程序,程序猿可以根据需要将各种程序加载到内存中运行
随着时代的发展,人们发现很多程序有很多共同的部分,人们把这些程序追加到监控程序中。初期的操作系统就这样诞生了。操作系统本身不是单独的程序,而是多个程序的集合体
9.2 要意识到操作系统的存在
在操作系统这个运行环境下,应用并不是直接控制硬件,而是通过操作系统来间接控制硬件
如用C语言编写一个显示当前时间的程序,操作系统收到应用发出的指令后,首先会对该指令进行解释,然后会对时钟IC(实时时钟)和显示器用的I/O进行控制
9.3 系统调用和高级编程语言的移植性
系统调用:操作系统的硬件控制功能,通常是通过一些小的函数集合体的形式来提供的。这些函数以及调用函数的行为称为系统调用
高级编程语言的机制是使用独自的函数名,然后再在编译时将其转换成相应操作系统的系统调用。即用高级编程语言编写的应用在编译后,就转换成了利用系统调用的本地代码
9.4 操作系统和高级编程语言使硬件抽象化
文件是操作系统对磁盘媒介空间的抽象化。如果直接对硬件进行操作的话,那就变成了通过向磁盘用的I/O指定扇区位置来对数据进行读写了
9.5 Windows操作系统的特征
32/64位操作系统
这里的32位表示处理效率最高的数据大小
32位操作系统,需要1次就可以完成32位的数据处理
通过API函数集来提供系统调用
API是联系系统应用程序和操作系统之间的接口
提供采用了GUI的用户界面
GUI中用户按照怎样的顺序操作是无法确定的。程序员必须制作出在任何操作顺序下都能正常运行的应用
通过WYSIWY提供打印输出
WYSIWYG是What You See Is What You Get的略写。意思是:显示器上显示的文本及图形等(What You See),是(ls)可以原样输出到打印机上(What You Get)的
提供多任务功能
多任务指的是同时运行多个程序的功能
WIndows是通过时钟分割技术来实现多任务功能的
时钟分割指的是在短时间间隔内,多个程序切换运行的方式。在用户看来,就是多个程序在同时运行,即WIndows会自动切换多个程序的运行
Windows中还具有程序中的函数为单位来进行时钟分割的多线程功能
线程是操作系统分配给CPU的最小运行单元
源代码的一个函数就相当于一个线程
多线程是指在一个程序中同时运行多个函数的意思
提供网络功能及数据库功能
网络功能和数据库功能虽然不是操作系统本身不可缺少的功能, 但是他们和操作系统很接近,所以被通称为中间件而不是应用
操作系统和中间件合在一起,也称为系统软件
通过即插即用实现设备驱动的自动设定
即插即用指的是新的设备连接后立即就可以使用的机制
新的设备连接到计算机后,系统会自动安装和设定用来控制该设备的设备驱动程序
设备驱动是操作系统的一部分,提供了同硬件进行基本的输入输出的功能。
键盘,鼠标、显示器、磁盘等装置,这些计算机中必备的硬件的设备驱动,一般都是随操作系统一起安装的
10.通过汇编语言了 解程序的实际构成
基础知识
本地代码加载到内存后才能运行。内存中存储着构成本地代码的指令和数据。程序运行时,CPU会从内存中把指令和数据读出,然后再将其存储在CPU内部的寄存器中进行处理
10.1 汇编语言和本地代码是一一对应的
汇编语言
本地代码是数值的罗列,直接使用这些数值来编程不太现实,在本地代码中加上表示其功能的助记符,这就是汇编语言
用汇编语言编写的源代码,最终要利用汇编器转换成本地代码才能运行
用汇编语言编写的源代码和本地代码是一一对应的
10.2 通过编译器输出汇编语言的源代码
大部分C语言编译器,都可以把利用C语言编写的源代码转换成汇编语言的源代码,而不是本地代码。利用该功能就可以得到汇编语言的源代码
10.3 不会转换成本地代码的伪指令
汇编语言的源代码,是由转换成本地代码的指令和针对汇编器的伪指令构成的
伪指令负责把程序的构造以及汇编的方法指示给汇编器。不过伪指令本身是无法汇编转换成本地代码的
10.4 汇编语言指令的语法结构是操作码+操作数
操作码表示指令动作
操作数表示指令对象
10.5 对栈进行Pushh和Pop
程序运行时,会在内存上申请分配一个名为栈的数据空间
栈是存储临时数据的区域,它的特点是通过push指令和pop指令进行数据的存储和读出
32位系列的CPU中,进行1次push或pop,即可处理32位(4字节)的数据
push和pop指令中只有一个操作数,表示push的是什么及pop的是什么,不需要指定对哪个地址编号的内存进行操作,因为对栈进行读写的内存地址有专门的寄存器(esp)进行管理
push指令和pop指令运行后,esp寄存器的值会自动进行更新(push指令是-4,pop指令是+4),因而程序猿没有必要指定内存地址里
10.6 函数内部的处理
函数的参数是通过栈来传递,返回值是通过寄存器来返回的
10.7 始终确保全局变量用的内存空间
C语言中在函数外部定义的变量称为全局变量,在函数内部定义的变量称为局部变量
全局变量可以在源代码任意部分被引用,而局部变量只能在定义该变量的函数内部进行引用
以Borland C++为例,转换成汇编后
编译后的程序,会被归类到名为段定义的组中
初始化的全局变量,会被汇总到名为DATA的段定义中
没有被初始化的全局变量,会被汇总到BSS的段定义中
在DATA和BSS中,全局变量的内存空间得到了确保
指令则被汇总到名为TEXT的段定义中
10.8 临时确保局部变量用的内存空间
局部变量是函数运行期间临时保存到寄存器和栈中的
函数内部利用的栈,在函数处理完毕后会恢复到初始状态,因此局部变量的值也被销毁了
只要寄存器有空间,编译器就优先使用它,因为与内存相比,使用寄存器时访问速度会高很多,这样就可以更快速的处理
Borland C++编译器自动优化有可能把局部变量分配到寄存器中,寄存器空间时使用寄存器,寄存器空间不足的话就使用栈
10.9 循环处理的实现方法
在汇编语言的源代码中,循环是通过比较指令(cmp)和跳转(jl)来实现的
10.10 条件分支的实现方法
条件是通过比较指令(cmp)和跳转(jl)来实现
比较结果小时跳转的jle(jump on less or equal)
大时跳转到jge(jump on greater or equal)
无论结果如何都无条件跳转的jmp
第十一章 硬件控制方法
基础知识
用来识别外围设备的编号称为I/O地址或I/O端口号。所有链接到计算机的外围设备都会分配一个I/O地址编号
IRQ是Interrupt Request。IRQ指的是用来执行硬件中断请求的编号
DMA是Direct Memory Access。DMA指的是,不经过CPU中介处理,外围设备直接同计算机的主内存进行数据传输。
用来识别具有DMA功能的外围设备的编号称为DMA通道。像磁盘这样用来处理大量数据的外围设备都具有DMA功能。
用软件控制硬件实际上是利用输入输出指令同外围设备进行输入输出的处理
计算机能处理的事情,始终只是对输入的数据进行运算,并把结果输出
11.1 应用与硬件无关?
在用C语言等高级编程语言开发的Windows应用中,大家很少能接触到直接控制硬件的指令。这是因为硬件的控制是由Windows全权负责的
Windows提供了间接控制硬件的方法。利用操作系统提供的系统调用功能就可以实现对硬件的控制
11.2 支撑硬件输入输出的IN指令和OUT指令
Window控制硬件时借助的是输入输出指令。其中具有代表性的两个输入输出指令是IN和OUT
IN指令通过指定端口号的端口输入数据,并将其存储在CPU内部的寄存器中
OUT指令则是把CPU寄存器中存储的数据,输出到指定端口号的端口
端口
计算机主机中,附带了用来连接显示器及键盘等外围设备的连接器。而连接器的内部,都有用来交换计算机主机同外围设备之间电流特性的IC
这些IC,统称为I/O控制器。由于电压不同,数字信号以及模拟信号的电流特性不同,计算机主机和外围设备之间无法直接连接。I/O控制器就可以解决这个问题
显示器、键盘等外围设备都有各自专用的I/O控制器。I/O控制器中有用于临时保存输入输出数据的内存。这个内存就是端口
I/O控制器内部的内存,也称为寄存器。虽然都是寄存器,但是用来它和CPU内部的寄存器不同,CPU内部的寄存器是来进行数据运算处理的,I/O寄存器主要用来临时存储数据的
端口号
在实现I/O控制器功能的I/C中,会有多个端口。由于计算机中连接着很多外围设备,所以就会有多个I/O控制器,当然会有多个端口
端口之间通过端口号进行区分,端口号也称为I/O地址
IN指令和OUT指令在端口号指定的端口和CPU之间进行数据的输入输出
11.3 外围设备的中断请求
IRQ(Interrupt Request)是中断请求的意思
中断处理
IRQ是用来暂停当前正在运行的程序,并跳转到其他程序运行的必要机制
中断处理在硬件控制中担当着重要角色。因为如果没有中断处理,就有可能出现处理无法顺畅进行的情况
从中断处理开始到请求中断的程序运行结束之前,被中断的程序(主程序)的处理是停止的
中断处理程序运行结束后,处理也会返回到主程序中继续
实施中断请求的是连接外围设备的I/O控制器,负责实施中断处理程序的是CPU
为了进行区分,外围设备中断请求会使用不同于I/O端口的其他编号,该编号称为中断编号
假如同时有多个外围设备进行中断请求的话,CPU也会为难。为此,我们可以在I/O控制器和CPU中间加入名为中断控制器的IC来进行缓冲。中断控制器会把从多个外围设备发出的中断请求有序地传递给CPU
CPU接收到来自中断控制器的请求后,会把当前中在运行的主程序中断,并切换到中断处理程序
中断处理程序的第一步处理,就是把CPU所有寄存器的值保存到内存的栈中
在中断处理程序中完成外围设备的输入输出后,把栈中保存的数据还原到CPU寄存器中,然后再继续进行对主程序的处理
11.4 DMA可以实现短时间内传送大量数据
DMA是指不通过CPU的情况下,外围设备直接和主内存进行数据传送。
磁盘等都用到了这个DMA机制。通过利用DMA,大量数据就可以在短时间内转送到主内存。因为CPU作为中介的时间被节省了
11.5 文字及图片显示机制
显示器中显示的信息一直存储在某内存中。该内存称为VRAM(Video RAM)。在程序中,只要往VRAM中写入数据, 该数据就会在显示器中显示出来。
在现代计算机中,显卡等专用硬件中一般都配置有与主内存相独立的VRAM和GPU(Graphics Processing Unit,图形处理器)。内存VRAM中存储的数据就是显示器上显示的信息
第十二章 让计算机思考
基础知识
伪随机数指的是指的是通过公式产生的随机]。伪随机数同真正的随机数不同,具有周期性
随机数的种子指的是生成伪随机数的公式中使用的参数。随机数的种子不同,产生的随机数也是不同
随机数色子
用来生成随机数的一种工具
一般程序会通过生成类似于随机数的数值形式来得到随机数
在C语言中,虽然该公式的实体是隐藏的,但只要调用rand()函数,就可以得到随机数
由于借助公式产生的随机数具有一定的规律性,因此并不是真正的随机数,通常称为伪随机数