学习笔记

第8章 从源文件到可执行文件

本章问题:

问题

本章重点:

编译器的功能;程序从源代码到可执行文件的流程;程序运行时的堆和栈。

8.1 计算机只能运行本地代码

一个例子1

一个例子2

图中栗子的源代码文件命名为Sample1.c。

源代码需要转换成本地代码才能运行

8.2 本地代码的内容

直接用记事本打开本地代码:

记事本打开本地代码

把本地代码Dump一下,每一个字节用2位16进制数(每个16进制数代表4位二进制数,2位16进制数恰好代表8位即1字节)来表示:

本地代码的真是面目是数值的罗列

8.3 编译器负责转换源代码

不同的语言有各自的编译器;

不同类的CPU有不同的机器语言,需要不同的编译器;

编译器也是应用程序,也需要运行环境;

存在交叉编译器,在某一环境下运行,可以生成另一环境下的本地代码。

通过命令行编译上面的栗子

8.4 仅靠编译是无法得到可执行文件的

编译生成的是.obj文件(目标文件),而不是.exe文件,无法直接运行;

如果源代码中引用了其它的函数(如上面例子中的sprintf()、MessageBox()),就需要把储存着这些函数的目标文件与此目标文件相结合;

完成此工作的是链接器,最后生成.exe文件

链接上面的栗子

8.5 启动及库文件

在链接的命令中,c0w32.obj记述的是同所有程序起始位置相结合的处理内容,称为程序的启动,即使未调用其它目标文件的函数,也必须要进行链接,并和启动结合起来;

扩展名为.lib的文件称为库文件,是多个目标文件的集合。链接器指定库文件后,会把需要的函数从库文件中提取出来,例子中的sprintf()储存在cw32.lib中,MessageBox()实际上是储存在user32.dll中(会在后面说明原因);

库文件可以简化链接过程,当需要很多个库函数时,只需要链接数个库文件就可以了;

sprintf()等函数,不通过源代码而是通过库函数和编译器一起提供,称之为标准函数,标准函数以目标文件形式集合在库文件中,不会暴露源码,可以避免造成商业损失。

8.6 DLL文件及导入库

Windows以函数的形式为应用提供了各种功能,称之为API(Application Programming Interface),上面的栗子中MessageBox()就是Windows提供的一种API;

Windows中的API并非储存在通常的库函数中,而是储存在DLL(动态链接库)中,DLL是程序运行时动态结合的文件;

与DLL相反,存储目标文件的实体,并直接与EXE结合的称之为静态链接库,例如cw32.lib;

通过导入库文件,EXE在执行时会从DLL调出函数的信息就会写在EXE中。

用下图总结下:

Windows中编译和链接机制

8.7 可执行文件运行时的必要条件

EXE文件中函数和变量的内存地址是如何来表示的呢?

在EXE文件的开头给函数和变量分配了虚拟内存地址,程序运行时,虚拟内存地址会转换为实际内存地址;

链接器会在开头,追加转换内存地址所必需的信息,这个信息称为再配置信息;

在配置信息,就成了函数和变量的相对地址;

在源代码中,函数和变量是分散记数的,在链接后的EXE中,函数和变量就会变成连续排列的组,这样就可以用相对于起始地址的偏移量来表示函数和变量。

链接后EXE的构造

8.8 程序加载时会生成堆和栈

程序加载到内存后,会生成栈和堆;

栈存储局部变量,堆用来存储程序运行时的任意数据及对象的内存区域;

栈中对数据进行存储和舍弃的代码,由编译器自动生成,而堆的内存空间则需要程序员明确申请分配或者释放,在高级语言中,编译器会自动生成指定栈和堆大小的代码;

加载到内存中的程序由4部分组成

8.9 有点难度的Q&A

问题答案:

答案

第9章 操作系统和应用的关系

本章问题:

问题

本章重点

9.1 操作系统功能的历史

操作系统的前身是监控程序,作用是加载和运行程序;

后来人们发现很多程序都有共同的部分,例如用键盘输入、用显示屏输出等,于是把这些程序也加入了监控程序当中;

于是,更多的有用程序被加入了监控程序中,渐渐变身成为了操作系统..........

监控程序是操作系统的雏形

初期的操作系统 = 监控系统 + 输入输出程序

操作系统是多个程序的集合体

9.2 要意识到操作系统的存在

应用程序通过操作系统间接向硬件发送指令。

比如printf()、time()函数运行的结果,是面向操作系统而非硬件的,操作系统接到指令后,首先解释这些指令,然后会对时钟IC和显示器的IO进行控制。

应用程序通过操作系统间接控制硬件

9.3 系统调用和高级编程语言的移植性

操作系统的硬件控制功能,通常是通过一些小的函数集合体的形式提供的,这些函数及调用这些函数的行为统称为系统调用;

C语言等高级语言一般不依赖于操作系统,因为认为希望不会因为操作系统的不同而重写大量的代码;所以高级语言使用独立的函数名,然后在编译时转换成相应的系统调用;

高级语言也可以直接进行系统调用,不过会影响可移植性;

高级编程语言的函数调用在编译后变成了系统调用

9.4 操作系统和高级编程语言使硬件抽象化

操作系统的系统调用,给程序的编写带来的巨大的方便。

9.5 Windows操作系统的特征

32位操作系统(书有点过时了):在过去的16位操作系统中,处理32位的数据类型相当于处理两次16位数据类型,要花费更多的时间;有了32位

系统,处理一次就够了,因此使用32位数据类型不会降低运行速度;

通过API函数集实现系统调用:API通过多个DLL文件来提供;

提供采用了GUI的用户界面:编写GUI较为困难,因为操作流程是由用户决定而非程序员决定,因此要考虑所有可能的情况;

通过WYSIWYG来打印输出;

提供多任务功能;

提供网络功能及数据库功能;

通过即插即用实现设备驱动的自动设定;

问题答案:

问题答案

第10章 通过汇编语言了解程序的实际构成

本章问题:

问题

本章重点:

当然是汇编。

10.1 汇编语言和本地代码是一一对应的

使用汇编语言有助于理解本地代码。直接打开本地代码只能看到数值的罗列,通过添加助记符,如加法运算add,比较运算cmp等,可以更好地理解本地代码。使用助记符的语言被称为汇编语言,通过查看汇编语言的源代码,可以更容易地理解本地代码。

汇编语言和本地代码是一一对应的,所以可以通过本地代码反汇编得到汇编代码。但是高级语言和本地代码不是一一对应的,所以反编译到高级语言比较困难,而且完全还原是不太可能的。

10.2 通过编译器输出汇编语言的源代码

原书作者通过编译命令把源代码输出为汇编代码,汇编文件的后缀为.asm(assemble)。

源代码

汇编

10.3 不会转换成本地代码的伪指令

汇编语言的源代码,是由转换成本地代码的指令(操作码)和针对汇编器的伪指令组成的。

伪指令负责把程序的构造及汇编的方法指示给汇编器,但是伪指令本身是无法转换成本地代码的。

伪指令

其中由伪指令segment和ends围起来的部分,是程序中命令和数据的集合体,称之为段定义。_TEXT、_DATA、_BSS是段定义的名称。

group表示把_BSS和_DATA这两个段定义汇总名为DGROUP的组。

_AddNum proc 和 _AddNum endp围起来的部分,是函数AddNum的范围,同理_MyFunc proc和MyFunc endp 围起来的部分表示函数MyFunc的范围。这两个函数都置于_TEXT中,表示属于_TEXT段定义。虽然源代码中的指令和数据比较混乱,但是通过段定义,汇编之后会转换成划分整齐的本地代码。

end表示源代码的结束。

10.4 汇编语言的意思是“操作码”+“操作数”

操作码是指令动作,操作数是指令对象。

常用操作码

主要寄存器

10.5 最常用的mov指令

mov指令中有两个操作数,分别指定数据的存储地和来源。

如果操作数没有用[]围起来,就表示对其值进行操作,否则的话会把值解释为内存地址,然后对相应地址中的值进行操作。

两种情况

10.6 对栈进行push和pop

栈是存储临时数据的区域,数据的读取要符合先进后出原则。

栈模型

10.7 函数调用机制

函数调用需要依赖栈的作用。

下图为在MyFunc函数中调用AddNum函数的处理内容:

函数调用

(1)、(2)、(7)、(8)的处理适用于C语言中所有函数。

(3)和(4)表示传递给AddNum的函数通过push入栈。

(5)的call指令把程序流程跳转到了操作数中指定的AddNum函数所在的内存地址,AddNum处理完毕后,程序流程会必须回到(6)这一行。可以在调用AddNum之前把(6)指令的内存地址入栈,调用完毕后ret指令再把(6)指令的内存地址pop出来,从而使程序流程回到(6);

(6)是把栈指针向高位移动两位,实际上就是使123和456出栈;

源代码中有个变量c指向AddNum(123,456)的运算结果,但是c变量在之后并没有用到,所以编译器就会自动优化,没有生成与之相关的汇编代码。

10.8 函数内部的处理

下图为call AddNum后AddNum函数内部的处理过程。

函数内部的处理

ebp在(1)、(5)中入栈、出栈,是为了把ebp的值还原到函数调用前的状态,因为ebp之前可能在其它地方被使用;

指令(2)中把栈指针寄存器esp的值赋给ebp,这是因为在mov指令中方括号[]中的参数不允许使用esp,所以要用ebp来代替;

使用栈中的数据是通过方括号中的ebp + 相应字节数来实现的,比如(3)中通过[ebp + 8]指定栈中的123,并用mov存储在eax中;

指令(4)中的add指令把123和456相加存储在eax中;

函数的参数通过栈来传递,返回值是通过寄存器来返回;

指令(6)的ret运行后,函数返回目的地的内存地址会自动出栈,程序流程就会跳至函数调用的下一行;

可按照图10-4、10-5中a、b、c、d、e、f的顺序来看函数调用时栈的状态变化。

10.9 始终确保全局变量用的内存空间

简单地说,在Borland C++中,初始化和非初始化的全局变量分别被划到两个不同的段定义中,未被初始化的变量都会被设定为0进行初始化。

10.10 临时确保局部变量用的内存空间

临时变量储存在寄存器和栈中。

由于寄存器的访问速度较快,寄存器空闲时就使用寄存器来存储局部变量,否则就用栈。

函数调用完毕后,栈中局部变量的值就会被销毁(通过恢复栈指针的方式)。

10.11循环处理的实现方法

循环源代码:

循环源代码

循环汇编代码

对ebx执行xor异或运算,使ebx清零,这比mov ebx 0 要快,ebx清零其实就等价于i=0;

ebx初始化后调用MySub,然后返回,进行第三行指令,把ebx加一;

cmp是把ebx的值与10比较,结果存储在标志寄存器中

jl 是jump on less than ,如果前面的比较指令的值是“小”的话,就跳转至@4处的指令,从而实现了循环。

10.12 条件分支的实现方法

与循环类似。

条件分支C++源代码:

条件分支汇编代码:

10.13 了解程序运行方式的必要性

了解程序运行方式有助于我们更好的理解程序出错的原因。

下面的代码是两个函数更新同一个全局变量:

实际的汇编代码:

可以看出,counter的值乘2是把counter的值读入累加寄存器后实现的。MyFunc1和MyFunc2都是把counter的值乘2,最后理应是4倍,但是MyFunc1运行时如果尚未来的及把eax中两倍的数值写入到counter中去MyFunc2就读取了counter的值,最后运算的结果是counter的值只变为了原来的两倍。

因此为了避免这种错误,我们可以采用以函数或者C语言代码的行为单位来禁止线程切换的锁定方法。

问题答案:

答案

第11章 硬件控制方法

本章提问:

问题

11.1 应用和硬件无关?

Windows应用通过调用Windows操作系统的API(系统调用)来间接控制硬件。

应用通过操作系统间接控制硬件

下面是一个

计算机是如何跑起来的百度云,《程序是怎样跑起来的》(下)相关推荐

  1. 计算机是如何跑起来的百度云,如何在一台电脑安装两个百度云盘

    在同一台正在运行的电脑上暂时没法同时安装两个百度云软件. 虽然不能同时安装两个百度云盘,但一个账号可以在百度云盘里面登录,另一个账号就在浏览器上面登录百度云. 百度云盘具有容量大.速度快.安全等特点, ...

  2. 计算机里的备份怎样存入百度云,百度网盘电脑怎么关闭备份

    打开客户端→→点击右上角设置→→再打开里面的设置→→基本→→自动备份:管理自动备份文件夹→→管理→→取消相关文件夹,手机端的话,打开"我的",在你的名字下面第一行第一个图标&quo ...

  3. 百度云曲显平:AIOps时代下如何用运维数据系统性地解决运维问题?

    百度云智能运维负责人 曲显平 本文是根据百度云智能运维负责人曲显平10月20日在msup携手魅族.Flyme.百度云主办的第十三期魅族技术开放日<百度云智能运维实践>演讲中的分享内容整理而 ...

  4. 万网空间 php伪静态,百度云虚拟主机zblogphp在Nginx环境下设置伪静态规则,极为重要...

    怎么设置都是404错误 在zblog插件的伪静态管理中心插件设置一番之后,由于以前我都是使用万网主机的,这一次是使用百度云虚拟主机,所以设置的方法也是不一样,这次是nginx环境,万网主机只要设置.h ...

  5. 百度云曲显平:AIOps时代下如何用运维数据系统性地解决运维问题? 1

    本文是根据百度云智能运维负责人曲显平10月20日在msup携手魅族.Flyme.百度云主办的第十三期魅族技术开放日<百度云智能运维实践>演讲中的分享内容整理而成. 内容简介:本文主要从百度 ...

  6. 计算机二级公共基础知识总结百度云,计算机二级公共基础知识总结详细版本[精]...

    计算机二级公共基础知识总结详细版本[精] (15页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 15.9 积分 全国计算机等级考试二级公共基础知识总结第一 ...

  7. 计算机二级36套题解答百度云,全国计算机二级C选择题试题库第36套

    全国计算机二级C选择题题库第36套 1.下列叙述中正确的是 A) 算法复杂度是指算法控制结构的复杂程度 B) 算法复杂度是指设计算法的难度 C) 算法的时间复杂度是指设计算法的工作量 D) 算法的复杂 ...

  8. 计算机二级web题库软件百度云,计算机等级考试题库,二级Web试题请你拿走拿走,别客气!...

    在这个天气凉爽的季节,在这个洒满阳光的日子里,考无忧小编想悄悄告诉你:下一次的计算机二级考试也很快到来的!赶紧刷计算机等级考试题库二级Web试题吧! 1.下列哪一个函数可以将数值型转换为字符串____ ...

  9. 计算机二级ms office真题百度云,历年计算机网络,计算机二级Word,Excel真题及答案...

    1. 客户/服务器模式的局域网,其网络硬件主要包括服务器.工作站.网卡和____. A.网络拓扑结构 B.计算机 C.传输介质D.网络协议 2. 和广域网相比,局域网____. A.有效性好但可靠性差 ...

最新文章

  1. LeetCode 1114. Print in Order--Java解法--并发问题
  2. linux vsftpd架设
  3. hdu5643 King's Game(约瑟夫环+线段树)
  4. ubuntu中安装hadoop集群
  5. VTK:图片之ImageFFT
  6. tinyhttpd源码详解
  7. JavaFX 2.0和Scala,像牛奶和饼干
  8. 一些最近发现的小问题
  9. 二叉树中序遍历的下一个节点
  10. 曼秀雷敦搜索引擎营销方案_搜索引擎营销——被严重低估的互联网营销途径
  11. 寒冬已至,传统零售业如何打破僵局“逆境生长”
  12. Centos 7.0 的svn
  13. Winform的菜单控件
  14. Gnuradio安装及ADALM-PLUTO设备的配置
  15. 纯前端实现—下拉菜单
  16. CentOS6.x安装方法超详细教程
  17. 漫画:如何分别使用8种编程语言拯救公主
  18. 更改WLAN的IP地址
  19. allegro制作通孔焊盘封装-flash热风焊盘-图文并茂的Allegro 通孔焊盘制作教程
  20. zedgraph显示最小刻度_ZedGraph 控件各属性以及示例

热门文章

  1. MacCms10潜藏后门分析
  2. 杭州到温州老家自驾路线优化与整理
  3. Autojs 微信添加好友脚本
  4. fragment添加失败错误查找
  5. CVPR 2022 Oral|港中文开源PoseC3D:基于3D-CNN的骨骼动作识别框架
  6. JS校验VIN码,生成虚拟VIN码
  7. matlab计算铰接式履带车辆转向性能
  8. Hive处理数据基本操作流程
  9. mysql 3358端口_好用的MySQL抓包工具:sniffer-agent
  10. 蓝牙BQB认证原因及流程