程序是怎样跑起来的
[!abstract] 程序是怎样跑起来的
- 书名: 程序是怎样跑起来的
- 作者: 矢泽久雄
- 简介: 日文版重印41次,“计算机组成原理”图解趣味版,蹲马桶就能看懂的编程基础知识!本书适合:菜鸟程序员入门进阶;中级程序员查漏补缺;高手程序员向家人(女友、老妈等)普及计算机知识。如何向小学生讲解CPU和二进制?如何向中学生讲解内存和磁盘?如何向女高中生讲解操作系统的原理?如何向老奶奶说明显示器和电视的不同?如果你完全没有思路,就应该读一读这本书。以图配文,深入讲解编程基础知识;语言通俗,即使是文科生也能看得懂。
- 出版时间 2015-04-01 00:00:00
- ISBN: 9787115385130
- 分类: 计算机-编程设计
- 出版社: 人民邮电出版社
高亮划线
1.1 CPU的内部结构解析
📌 CPU的内部由寄存器、控制器、运算器和时钟四个部分构成,各部分之间由电流信号相互连通。 ^907761-10-1198-1241
- ⏱ 2023-01-19 16:28:19
📌 寄存器可用来暂存指令、数据等处理对象,可以将其看作是内存的一种。根据种类的不同 ^907761-10-1260-1306
- ⏱ 2023-01-19 16:34:18
📌 程序启动后,根据时钟信号,控制器会从内存中读取指令和数据。通过对这些指令加以解释和运行,运算器就会对数据进行运算,控制器根据该运算结果来控制计算机。 ^907761-10-2436-2510
- ⏱ 2023-01-19 16:30:51
1.2 CPU是寄存器的集合体
📌 通常我们将汇编语言编写的程序转化成机器语言的过程称为汇编;反之,机器语言程序转化成汇编语言程序的过程则称为反汇编 ^907761-11-1239-1340
- ⏱ 2023-01-19 16:32:49
📌 寄存器中存储的内容既可以是指令也可以是数据。其中,数据分为“用于运算的数值”和“表示内存地址的数值”两种。数据种类不同,存储该数值的寄存器也不同。CPU中每个寄存器的功能都是不同的。用于运算的数值放在累加寄存器中存储,表示内存地址的数值则放在基址寄存器和变址寄存器中存储。代码清单1-1的程序中用到的eax和ebp分别是累加寄存器和基址寄存器。表1-1 寄存器的主要种类和功能[插图] ^907761-11-2806-3056
- ⏱ 2023-01-19 16:41:23
1.3 决定程序流程的程序计数器
- 📌 程序计数器决定着程序的流程。 ^907761-12-1421-1435
- ⏱ 2023-01-20 23:31:04
1.4 条件分支和循环机制
📌 但若程序中存在条件分支和循环,机器语言的指令就可以将程序计数器的值设定为任意地址(不是+1)。这样一来,程序便可以返回到上一个地址来重复执行同一个指令,或者跳转到任意地址。 ^907761-13-622-708
- ⏱ 2023-01-20 23:32:09
📌 无论当前累加寄存器的运算结果是负数、零还是正数,标志寄存器都会将其保存(也负责存放溢出和奇偶校验的结果)。 ^907761-13-1410-1686
- ⏱ 2023-01-20 23:33:53
1.5 函数的调用机制
- 📌 在编译高级编程语言的程序后,函数调用的处理会转换成call指令,函数结束的处理则会转换成return指令。这样一来,程序的运行也就变得非常流畅。 ^907761-14-2291-2363
- ⏱ 2023-01-22 00:37:23
1.6 通过地址和索引实现数组
1.7 CPU的处理其实很简单
📌 看完表后你会惊奇地发现,原来 CPU可以进行的处理非常少。虽然高级编程语言编写的程序看起来非常复杂,但CPU实际处理的事情就是这么简单。这样一来,大家是不是能够消除“计算机机制看起来很难”这个印象了呢? ^907761-16-571-672
- ⏱ 2023-01-22 00:44:30
📌 表1-2 机器语言指令的主要类型和功能① 外围设备指的是连接到计算机的键盘、鼠标、显示器、设备装置、打印机等。 ^907761-16-731-972
- ⏱ 2023-01-22 00:46:27
2.3 移位运算和乘除运算的关系
- 📌 十进制数左移后会变成原来的10倍、100倍、1000倍……同样,二进制数左移后就会变成原来的2倍、4倍、8倍……反之,二进制数右移后则会变成原来的1/2、1/4、1/8…… ^907761-20-2158-2244
- ⏱ 2023-01-22 18:09:07
2.5 逻辑右移和算术右移的区别
- 📌 不管是正数还是用补数表示的负数,都只需用符号位的值(0或者1)填充高位即可。这就是符号扩充的方法。 ^907761-22-1816-1865
- ⏱ 2023-01-22 20:45:00
3.2 用二进制数表示小数
- 📌 小数点后面部分的位权,第1位是2的-1次幂、第2位是2的-2次幂 ^907761-27-1192-1224
- ⏱ 2023-01-23 09:43:00
3.3 计算机运算出错的原因
- 📌 计算机之所以会出现运算错误,是因为“有一些十进制数的小数无法转换成二进制数”。 ^907761-28-492-531
- ⏱ 2023-01-23 09:43:52
3.4 什么是浮点数
- 📌 双精度浮点数类型用64位、单精度浮点数类型用32位来表示全体小数 ^907761-29-603-694
- ⏱ 2023-01-23 09:52:36
3.8 二进制数和十六进制数
- 📌 二进制数的4位,正好相当于十六进制数的1位。 ^907761-33-591-613
- ⏱ 2023-01-23 17:08:20
4.1 内存的物理机制很简单
- 📌 图4-1是内存IC(在这里假设它为RAM[插图])的引脚配置示例。虽然这是一个虚拟的内存IC,但它的引脚和实际的内存IC是一样的。VCC和GND是电源,A0~A9是地址信号的引脚,D0~D7是数据信号的引脚,RD和WR是控制信号的引脚。将电源连接到VCC和GND后,就可以给其他引脚传递比如0或者1这样的信号。大多数情况下,+ 5V的直流电压表示1,0V表示0。 ^907761-35-758-1111
- ⏱ 2023-01-23 21:20:33
4.3 简单的指针
4.4 数组是高效使用内存的基础
- 📌 由于在C语言中,数组的索引是从0开始的,因此,char g[100];表示的就是可以使用g[0]~g[99]这100个元素。 ^907761-38-923-985
- ⏱ 2023-01-23 21:34:37
4.6 链表使元素的追加和删除更容易
- 📌 接下来介绍的链表和二叉查找树,都是不用考虑索引的顺序就可以对数组元素进行读写的方式。通过使用链表,可以更加高效地对数组数据(元素)进行追加和删除处理。而通过使用二叉查找树,则可以更加高效地对数组数据进行检索。 ^907761-40-444-548
- ⏱ 2023-01-23 21:43:13
4.7 二叉查找树使数据搜索更有效
📌 二叉查找树[插图]是指在链表的基础上往数组中追加元素时,考虑到数据的大小关系,将其分成左右两个方向的表现形式。例如,假设我们事先把50这个值保存到了数组中。那么,如果接下来的值比先前保存的数值大的话,就要将其放到右边,反之如果小的话就放在左边。但实际的内存并不会分成两个方向,这是在程序逻辑上实现的(图4-15)。[插图]图4-15 二叉查找树的模型(将树颠倒后的形状) ^907761-41-461-1000
- ⏱ 2023-01-23 21:46:50
📌 只要在程序开发中多花一些心思,我们就可以熟练地使用内存、实现栈处理、链表处理、二叉查找树处理等,这一点想必大家都清楚了。不过,大家还必须理解为什么要进行这些处理。 ^907761-41-1649-1730
- ⏱ 2023-01-23 21:49:32
第5章 内存和磁盘的亲密关系
📌 磁盘缓存是指,把从磁盘中读出的数据存储在内存中,当该数据再次被读取时,不是从磁盘而是直接从内存中高速读出。 ^907761-42-1543-1596
- ⏱ 2023-01-24 13:35:09
📌 利用电流来实现存储的内存,同利用磁效应来实现存储的磁盘,还是有差异的。而从存储容量来看,内存是高速高价,而磁盘则是低速廉价。 ^907761-42-2089-2151
- ⏱ 2023-01-24 13:36:32
5.1 不读入内存就无法运行
- 📌 磁盘中存储的程序,必须要加载到内存后才能运行。在磁盘中保存的原始程序是无法直接运行的。这是因为,负责解析和运行程序内容的CPU,需要通过内部程序计数器来指定内存地址,然后才能读出程序。 ^907761-43-689-855
- ⏱ 2023-01-24 23:35:07
5.2 磁盘缓存加快了磁盘访问速度
📌 磁盘缓存指的是把从磁盘中读出的数据存储到内存空间中的方式。 ^907761-44-598-634
- ⏱ 2023-01-24 23:35:56
📌 把低速设备的数据保存在高速设备中,需要时可以直接将其从高速设备中读出,这种缓存的方式在其他情况下也会用到。 ^907761-44-1154-1233
- ⏱ 2023-01-24 23:51:31
5.3 虚拟内存把磁盘作为部分内存来使用
📌 虚拟内存是指把磁盘的一部分作为假想的内存来使用。这与磁盘缓存是假想的磁盘(实际上是内存)相对,虚拟内存是假想的内存(实际上是磁盘)。 ^907761-45-518-591
- ⏱ 2023-01-24 23:52:51
📌 虚拟内存的方法有分页式和分段式两种。Windows采用的是分页式。该方式是指,在不考虑程序构造的情况下,把运行的程序按照一定大小的页(page)进行分割,并以页为单位在内存和磁盘间进行置换。在分页式中,我们把磁盘的内容读出到内存称为Page In,把内存的内容写入磁盘称为Page Out。一般情况下,Windows计算机的页的大小是4KB。也就是说,把大程序用4KB的页来进行切分,并以页为单位放入磁盘(虚拟内存)或内存中(图5-3)。 ^907761-45-970-1372
- ⏱ 2023-01-24 23:55:07
5.4 节约内存的编程方法
📌 (1)通过DLL文件实现函数共有 ^907761-46-1236-1252
- ⏱ 2023-01-24 23:57:36
📌 (2)通过调用_stdcall来减小程序文件的大小 ^907761-46-2862-2887
- ⏱ 2023-01-24 23:59:08
6.4 通过莫尔斯编码来看哈夫曼算法的基础
- 📌 夫曼算法的关键就在于“多次出现的数据用小于8位的字节数来表示,不常用的数据则可以用超过8位的字节数来表示” ^907761-52-835-888
- ⏱ 2023-01-25 00:22:58
7.7 BIOS和引导
📌 引导程序是存储在启动驱动器起始区域的小程序。 ^907761-64-620-649
- ⏱ 2023-01-25 23:20:59
📌 引导程序的功能是把在硬盘等记录的OS加载到内存中运行。虽然启动应用是OS的功能,但OS并不能自己启动自己,而是通过引导程序来启动。 ^907761-64-747-812
- ⏱ 2023-01-25 23:22:02
8.2 本地代码的内容
- 📌 Dump是指把文件的内容,每个字节用2位十六进制数来表示的方式。 ^907761-67-1032-1071
- ⏱ 2023-01-26 00:11:21
8.5 启动及库文件
- 📌 sprintf()等函数,不是通过源代码形式而是通过库文件形式和编译器一起提供的。这样的函数称为标准函数。之所以使用库文件,是为了简化为链接器的参数指定多个目标文件这一过程。 ^907761-70-2123-2236
- ⏱ 2023-01-26 00:20:17
8.7 可执行文件运行时的必要条件
📌 那就是EXE文件中给变量及函数分配了虚拟的内存地址。在程序运行时,虚拟的内存地址会转换成实际的内存地址。链接器会在EXE文件的开头,追加转换内存地址所需的必要信息。这个信息称为再配置信息。 ^907761-72-834-954
- ⏱ 2023-01-26 00:23:22
📌 EXE文件的再配置信息,就成为了变量和函数的相对地址 ^907761-72-983-1009
- ⏱ 2023-01-26 00:23:42
📌 相对地址表示的是相对于基点地址的偏移量,也就是相对距离。 ^907761-72-1010-1038
- ⏱ 2023-01-26 00:23:47
8.8 程序加载时会生成栈和堆
📌 程序加载时会生成栈和堆 ^907761-73-397-408
- ⏱ 2023-01-26 00:25:13
📌 在内存的使用方法上,二者存在些许不同。栈中对数据进行存储和舍弃(清理处理)的代码,是由编译器自动生成的,因此不需要程序员的参与。使用栈的数据的内存空间,每当函数被调用时都会得到申请分配,并在函数处理完毕后自动释放。与此相对,堆的内存空间,则要根据程序员编写的程序,来明确进行申请分配或释放。 ^907761-73-1559-1704
- ⏱ 2023-01-26 23:45:08
8.9 有点难度的Q&A
- 📌 编译器是在运行前对所有源代码进行解释处理的。而解释器则是在运行时对源代码的内容一行一行地进行解释处理的。 ^907761-74-516-568
- ⏱ 2023-01-26 23:46:22
第9章 操作系统和应用的关系
- 📌 .监控程序也可以说是操作系统的原型。 ^907761-75-1465-1483
- ⏱ 2023-01-26 23:49:21
9.1 操作系统功能的历史
📌 有人开发出了仅具有加载和运行功能的监控程序,这就是操作系统的原型。 ^907761-76-755-814
- ⏱ 2023-01-26 23:50:54
📌 操作系统本身并不是单独的程序,而是多个程序的集合体 ^907761-76-1808-1833
- ⏱ 2023-01-26 23:51:51
9.2 要意识到操作系统的存在
📌 要想成为一个全面的程序员,有一点需要清楚的是,掌握基本的硬件知识,并借助操作系统进行抽象化,可以大大提高编程效率。 ^907761-77-692-749
- ⏱ 2023-01-26 23:53:13
📌 在操作系统这个运行环境下,应用并不是直接控制硬件,而是通过操作系统来间接控制硬件的。变量定义中涉及的内存的申请分配,以及time()和printf()这些函数的运行结果,都不是面向硬件而是面向操作系统的。操作系统收到应用发出的指令后,首先会对该指令进行解释,然后会对时钟IC(实时时钟)和显示器用的I/O进行控制。 ^907761-77-2076-2355
- ⏱ 2023-01-26 23:54:51
9.3 系统调用和高级编程语言的移植性
- 📌 高级编程语言的机制就是,使用独自的函数名,然后再在编译时将其转换成相应操作系统的系统调用(也有可能是多个系统调用的组合) ^907761-78-854-914
- ⏱ 2023-01-26 23:56:22
10.1 汇编语言和本地代码是一一对应的
📌 这些缩写称为助记符,使用助记符的编程语言称为汇编语言。 ^907761-83-761-840
- ⏱ 2023-01-27 16:00:36
📌 不过,即使是用汇编语言编写的源代码,最终也必须要转换成本地代码才能运行。负责转换工作的程序称为汇编器,转换这一处理本身称为汇编。在将源代码转换成本地代码这个功能方面,汇编器和编译器是同样的。 ^907761-83-921-1068
- ⏱ 2023-01-27 16:00:51
10.2 通过编译器输出汇编语言的源代码
- 📌 汇编语言源文件的扩展名,通常用“.asm”来表示 ^907761-84-2084-2108
- ⏱ 2023-01-27 16:03:21
10.3 不会转换成本地代码的伪指令
- 📌 汇编语言的源代码,是由转换成本地代码的指令(后面讲述的操作码)和针对汇编器的伪指令构成的。 ^907761-85-569-640
- ⏱ 2023-01-27 16:05:00
10.4 汇编语言的语法是“操作码+操作数”
- 📌 表10-2 x86系列CPU的主要寄存器 ^907761-86-2037-2369
- ⏱ 2023-01-27 16:14:07
10.5 最常用的mov指令
- 📌 dword ptr(double word pointer)表示的是从指定内存地址读出4字节的数据 ^907761-87-954-1003
- ⏱ 2023-01-27 16:09:59
10.7 函数调用机制
📌 函数调用是栈发挥大作用的场合。 ^907761-89-533-548
- ⏱ 2023-01-27 16:12:17
📌 在汇编语言中,函数名表示的是函数所在的内存地址 ^907761-89-1369-1392
- ⏱ 2023-01-27 16:17:14
10.8 函数内部的处理
📌 (2)中把负责管理栈地址的esp寄存器的值赋值到了ebp寄存器中。这是因为,在mov指令中方括号内的参数,是不允许指定esp寄存器的。因此,这里就采用了不直接通过esp,而是用ebp寄存器来读写栈内容的方法。 ^907761-90-1055-1159
- ⏱ 2023-01-27 16:25:28
📌 函数的参数是通过栈来传递,返回值是通过寄存器来返回的 ^907761-90-1503-1536
- ⏱ 2023-01-27 16:22:21
10.13 了解程序运行方式的必要性
📌 在多线程处理中,用汇编语言记述的代码每运行1行,处理都有可能切换到其他线程(函数)中。 ^907761-95-2088-2131
- ⏱ 2023-01-27 16:30:20
📌 为了避免该bug,我们可以采用以函数或C语言源代码的行为单位来禁止线程切换的锁定方法。通过锁定,在特定范围内的处理完成之前,处理不会被切换到其他函数中。 ^907761-95-2685-2787
- ⏱ 2023-01-27 16:30:36
📌 没有汇编语言经验的程序员,就相当于只知道汽车的驾驶方法而不了解汽车结构的驾驶员。对这样的驾驶员来说,如果汽车出现了故障或奇怪的现象,他们就无法自己找到原因。不了解汽车结构的话,开车的时候还可能会浪费油。这样的话,作为职业驾驶员是不合格的。与此相对,有汇编语言经验的程序员,也就相当于了解计算机和程序机制的驾驶员,他们不仅能自己解决问题,还能在驾驶过程中省油。 ^907761-95-3047-3226
- ⏱ 2023-01-27 16:31:14
11.2 支撑硬件输入输出的IN指令和OUT指令
📌 I/O 控制器。由于电压不同,数字信号及模拟信号的电流特性也不同,计算机主机和外围设备是无法直接连接的。为了解决这个问题,I/O控制器就很有必要了。 ^907761-98-1159-1240
- ⏱ 2023-01-27 16:35:04
📌 I/O控制器中有用于临时保存输入输出数据的内存。这个内存就是端口。 ^907761-98-1314-1373
- ⏱ 2023-01-27 16:35:24
11.3 编写测试用的输入输出程序
- 📌 在大部分C语言的处理(编译器的种类)中,只要使用_asm{和}括起来,就可以在其中记述助记符。也就是说,这样就可以编写C语言和汇编语言混合的源代码 ^907761-99-631-704
- ⏱ 2023-01-27 16:38:30
11.4 外围设备的中断请求
📌 中断处理在硬件控制中担当着重要角色。因为如果没有中断处理,就有可能出现处理无法顺畅进行的情况 ^907761-100-664-710
- ⏱ 2023-01-27 16:42:16
📌 实施中断请求的是连接外围设备的I/O控制器,负责实施中断处理程序的是CPU。 ^907761-100-1258-1296
- ⏱ 2023-01-27 16:42:42
12.4 程序生成随机数的方法
- 📌 在C语言中,虽然该公式的实体是隐藏的,但只要调用rand()函数,就可以得到结果(随机数)。不过,由于借助公式产生的随机数具有一定的规律性,因此并不是真正的随机数,通常称为伪随机数。不过,虽然是伪随机数,仍然十分有用。 ^907761-109-1011-1146
- ⏱ 2023-01-27 16:46:03
读书笔记
1.2 CPU是寄存器的集合体
划线评论
- 📌 程序是把寄存器作为对象来描述的。 ^37417557-7FnM5yINq
- 💭 编程: 面向寄存器编程 or 编写寄存器能理解的文本
- ⏱ 2023-01-19 16:36:40
1.6 通过地址和索引实现数组
划线评论
- 📌 CPU则会把基址寄存器+变址寄存器的值解释为实际查看的内存地址。变址寄存器的值就相当于高级编程语言程序中数组的索引功能。 ^37417557-7Frkzw2Ue
- 💭 转
基址寄存器存储数组中打头的那个元素在内存中的实际地址,而变址寄存器存储的是元素索引。两者结合,就能准确找到所有的元素。 - ⏱ 2023-01-22 00:43:12
- 💭 转
4.3 简单的指针
划线评论
- 📌 代码清单4-2 各种数据类型指针的定义[插图]假设d、e、f的值都是100。在这种情况下,使用d时就能够从编号100的地址中读写1个字节的数据,使用e时就是2个字节(100地址和101地址)的数据,使用f时就是4个字节(100地址~103地址)的数据。怎么样?指针是不是很简单呢(图4-5)。 ^37417557-7FuaL82l3
- 💭 指针也有类型的意义
- ⏱ 2023-01-23 21:33:22
7.7 BIOS和引导
划线评论
- 📌 Bootstrap的原意是指靴子上部的“拔靴带”。BIOS这样小的程序(拔靴带),可以带动(启动)操作系统这样的大程序(靴子),所以由此得名 ^37417557-7FxkD681F
- 💭 原来是这么来的
- ⏱ 2023-01-25 23:24:06
8.3 编译器负责转换源代码
划线评论
- 📌 还有一种交叉编译器,它生成的是和运行环境中的CPU不同的CPU所使用的本地代码。 ^37417557-7FxnSVeIF
- 💭 例如golang
- ⏱ 2023-01-26 00:13:48
本书评论
书评 No.1
经典书籍 ^37417557-7FzXvNhwS
⏱ 2023-01-27 16:51:08