[组原]《程序是怎样运行的》 笔记
来源:《程序是怎样运行的》
文章目录
- 程序是在何种环境运行的
- 从源文件到可执行文件
- CPU是什么
- 小数运算出错
- 操作系统和应用的关系
- 内存
- 内存和磁盘的关系
程序是在何种环境运行的
运行环境 = 操作系统 + 硬件。
拿Windows说吧。
Windows 的前身是 MS-DOS (Microsoft-Disk operator system)
那时候DOS操作系统的功能还不健全,也没有统一标准。各家CPU厂商的软件是不能互相移植的(比如当时最火的AT兼容机和PC9081就不能兼容)。买软件光盘时也要根据操作系统区分。
不能兼容的主要原因:
- 上面提到的,DOS的功能不健全
- 软件应用会直接操控硬件,来达到最佳的运行效果。
(PC9081是为了提高AT兼容机打游戏的效果搞出来的。。。)
关键在于应用要直接操控硬件。随便一个小软件都需要操控底层硬件的权利。
而Window3.0的出现打破了这种局面。软件不再有直接操控硬件的权利,而是通过给操作系统(windows)发送指令间接操控。操控方式称作API。
这样一来,即使CPU不同,我们拥有相同的操作系统,相同的WINDOWS,就依然能共同运行一种软件。
Windows能解决软件兼容性问题,但解决不了CPU兼容问题。你还是要根据不同的CPU安装对应的Windows操作系统版本,但这无伤大雅。
API还是很方便的,只要操作系统相同,基本上能忽视硬件差异来运行软件。针对操作系统的API在任何硬件上都能运行。
同样的应用程序,移植到其他操作系统,只需要更改涉及到操作系统的部分即可。
就如同CPU类型不同,产生的机器语言不同;操作系统不同,API也不同。
科普:API 应用程序接口
应用程序向操作系统传递指令的途径 即API。
api 函数的信息存储在dll文件中,所有源文件编译后的目标文件能够共同使用一个dll文件,非常方便。这也是动态链接的优越性。
像键盘鼠标显示器的输入输出功能,都是API提供的。
api 还包含系统调用(system call)的函数。
总之,WINDOWS和它的API克服了CPU以外的硬件差异。
扩展:
Unix的操作系统FreeBSD,有种ports机制,能结合当前硬件环境来编译源代码,生成的本地代码就能运行。当然虚拟机也可以。
提到虚拟机,可以聊一聊java虚拟机(JavaVM)。java虚拟机和java是两码事,一个高级语言,一个运行环境。
JavaVM的思路就是编译后生成“字节代码”而非本地代码,这个字节代码只要环境是JavaVM,所有硬件都能移植。经过字节代码再变换成本地代码。
缺点是虽然字节代码可移植,JavaVM自己却难以移植。而且编译过程多了一步降低了效率。
BIOS
BIOS存在ROM中,控制硬件像键盘显卡磁盘等控制程序外,还掌管启动“引导程序”的功能。引导程序存储在启动驱动器起始区域,是启动OS的关键(OS不能自己启动自己)。
BIOS像boostrap,拔靴带一样,管理着庞大的OS。
从源文件到可执行文件
以C语言举例。
源文件到可执行文件经历三个阶段:
源文件(source file) —编译 → 目标文件(.obj , object) —链接→执行文件(.exe ,executable)
源文件到目标文件,即从 遵循高级语言规则,能用文本文档打开 到 “计算机底层的二进制代码,根本不知所云”。
目标文件才是属于计算机的语言。编译就好像翻译,把我们人类的信息传递给了计算机,语种是高级语言到二进制代码(物理层面甚至能用卡带来表示)。
编译器种类受三个因素影响:语言类型,操作系统类型,CPU类型。
这三个因素不同,编译后生成的本地代码是不同的。
目标文件的内容即“本地代码”,或者说计算机看得懂的代码。
本地代码中的 本地,译自 native ,母语。计算机母语。
有种交叉编译期能根据不同环境编译成不同目标代码。
这样看来,源文件或者说源代码提供了强大的可移植性。
目标文件到执行文件,需要源文件编译后得到的obj文件,还需要其他文件的帮助。
比如我的obj会用到一些内置函数,函数里有一些参数和返回值(函数签名),成为执行文件意味着计算机需要把这些函数和数据串通起来,形成真正能够处理特定工作的“程序”。
其他文件:
- obj
这些obj 包括启动程序时所需要的666.obj
(化名啦)。 - lib / dll
lib文件:library,两种形式:导入库或静态链接库。
dll文件:dynamic link library ,即动态链接库。
有动态链接库就有静态链接库 static link library。不过静态链接库文件可不是sll,而是直接以 lib 后缀文件,即导入库的形式出现。
库文件是把多个目标文件集合到一起的文件。它集合了很多库函数信息,像sprintf()
这种函数并非源代码手搓的,而是库文件中取出的,我们称之为标准函数。
源文件转成的那个目标文件里除了主体代码外,还会包含调用库函数的信息(类>似“请调用sprintf”这种信息)和 包含该标准函数信息的文件。
- 静态链接
静态链接库文件包含对应函数信息,配合源文件转成的目标文件的信息(对应函数的目标文件)能完成函数调用。
导入库要配合动态链接库文件,因为导入库文件里没有函数实体的目标文件,而是存储着该函数信息所在的dll文件信息,和这个dll文件所在的文件夹位置。 - 动态链接
导入库为目标文件静态链接的信息和目标文件原本调用库函数的信息,能配合动态链接库所属的dll文件,使链接器根据这些信息,知道要在dll文件的人山人海里找到需要的函数信息。
exe文件中有函数和变量的相对地址,称为再配置信息。函数和变量分散排列,但在链接成运行文件后,能根据偏移地址形成一个连续排列的组。
CPU是什么
从构造角度,CPU和内存是由许多晶体管组成的电子部件,成为IC(integrated Circuit,集成电路)。
逻辑上,CPU由寄存器、控制器、运算器和时钟四个部分构成,通过电信号连通。
寄存器:暂存指令、数据等,也是内存的一种
控制器:把内存是的指令数据读入寄存器,根据执行结果对计算机控制(In / Out)
运算器:运算内存读入寄存器的数据
时钟:发出CPU计时的时钟信号。
寄存器
程序将寄存器当作对象描述和处理,机器语言级别的程序都是通过寄存器来处理。
内存的存储场所通过地址编号区分,寄存器种类通过名字区分。
总之你可以把CPU看做各种功能的寄存器的逻辑集合体。
寄存器的类型:累加、标志(flag)、程序(PC)、基址、变址、通用、指令、栈
机器语言指令
CPU处理指令不多,只是组合起来能够产生惊人的效果。
1.数据传送
2.运算
3.跳转
4.call/return
小数运算出错
出错原因
计算机在小数运算上出错是必然的。因为任何进制表示数据都有其盲区。十进制中,10/3就不能用小数方式表示出来,所谓的科学计数法也不行。二进制里,小数尾数除5之外的基本都表示不了。
所以计算机中0.1加100次的结果不是10,正如 0.333…加3次的结果不会是10。
底层表示:浮点数
浮点数无外呼四部分:符号、尾数、基数、指数。
双精度相比单精度只是尾数和指数的表示范围大了些。
符号和基数不谈。
基数当然是二进制。总之就是负责形式上的“左移右移”,数值上的进位退位。
尾数:将小数点前面的值固定为1 的正则表达式
把位问题交给指数处理,尾数只负责完整表示数据的形式数值。
所以,通过左移、右移将数据转换成 前面值仅为1的形式。如:1011.0011 → 1.01101100000000…(补0齐位)
0.011 → 1.1000000…(补0齐位)指数:EXCESS 系统表现
一个很好的例子:
A~K的扑克牌表示1-13, 将7设为0 , 则此时10为+3, 3是-4 ;
AK表示-6+7 (你可能想到了补码)
操作系统和应用的关系
前言:
操作系统开始是用来监视任务执行的,后来为了方便加入了一些配合硬件的O/I功能和处理编译语言的功能,总的来说,操作系统是控制程序、实用程序、编译语言处理器的集合。
这里也可以看出,操作系统是硬件和软件的中间地带,是两者有序沟通的桥梁。
比如一个C语言的可执行文件,用来显示当前时间。它的一些指令代码(malloc , time() …)通过向操作系统发出指令来控制硬件(IC时钟)
系统调用:
操作系统控制硬件的功能来自于系统调用。系统调用是个函数,直接与硬件操作相关联。不过这种操作偏向底层,不会常被使用高级语言的我们所看到,因为系统调用函数往往在高级语言函数内部偷偷调用。
操作系统的存在也让高级语言函数和系统调用之间有了缓冲地带,即高级语言能够不依赖于操作系统,在任何场景中都能就地生成本地代码,来配合对应的系统调用。
直接调用系统调用会降低移植性。
调用Windows system call 的应用在 linux上就不适用。
硬件抽象化:
如上,高级语言和操作系统的配合,使硬件被抽象化了。我们能对os里的一个个文件进行直接操作,而文件恰恰是磁盘媒介空间的抽象化。或者说,文件是硬盘某个扇区的代名词。
对文件的操作是借助文件指针实现的。打开文件后,os会自动申请分配空间来管理文件读写操作。这个空间的地址在C语言中能通过fopen()
获得。
全面认识操作系统:
既然是全面认识,就要搞清楚操作系统本身的特点。以Windows为例:
32位操作系统(64位)
一次处理数据的大小。
通过API函数集提供系统调用
API是os和应用程序间的接口,Windows通过API的函数集提供函数调用。
Win32 API 中,个函数的参数和返回值数据大小,基本上都是32
GUI用户界面(linux是cli)
GUI操作的无序性(用户决定处理流程)使它编写起来有点棘手。
打印输出是所见即所得
显示器显示内容能直接打印出来。
有多任务功能
Windows通过时钟分割来同时运行多个任务,即切换运行多个程序。
windows还有以函数为单位运行程序的多线程功能。
支持网络和数据库功能
这哥俩是中间件,和os无关但比较紧密。os和中间件统称系统软件。
即插即用实现设备驱动的自动设定
我们购买的硬件设备中,都会附带软盘和CD-ROM,里面收录着设备驱动。
DLL文件可能也会同设备驱动文件一起安装。
DLL里会存着硬件本身的API。应用能通过API操作这些硬件。
内存
构造
内存从构造上说,是一种名为 内存IC 的电子元件。包括DRAM、SRAM、ROM等形式,但机制基本一样。
内存IC中有电源、地址信号、数据信号、控制信号等用于输入输出的大量IC引脚,这样可以表示地址和进行数据读写。
就是在数电中学的那些东西,芯片之类的玩意儿。
IC地址信号引脚越多,表示范围越大
如果IC有10个引脚,就能最多表示10位数据,容量即1kb(1024b)
现在的内存IC有更多引脚,一个就能存储MB甚至GB的容量。
注意
内存的存储,需要注意
- 数据类型,数据类型不同所需内存长度不同,读取操作涉及的内存长度也就不同。
- 大端法、小端法:多字节数据的 低位字节 存储在 内存低位地址 称为低字节序。
- 齐位要求:方便CPU的读取,对数据存储长度进行限制。像4的倍数之类的。
应用
队列的实现:环形缓冲区。
主存
内存,最主要的还是主存(main memory) ,主存控制芯片和CPU相连,存储指令数据。
存储空间里的每个字节有地址编号
内存和磁盘的关系
内存和磁盘都属于存储部件。
但内存利用电流存储,磁盘里用磁效应存储。
这意味着内存高速小容量,因为高速高价。磁盘低速大容量,因为低速低价。
前提
探讨关系前,先给一个前提:磁盘中的程序,必须读入到内存才能运行。
原因:CPU里有一个PC(programm counter)程序计数器,来指向运行目标内存地址。这意味着计算机体系里,程序的运行必须要借助内存。
磁盘作为存储部件,也可以要求CPU读取磁盘运行程序。但磁盘的构造决定其读取速度很慢,不适>用于程序运行,只适用于程序存储。
不过再多想些:为什么程序就得在磁盘里?
远古时期更换运行程序,要通过改变计算机布线实现。把程序存储在存储部件里有序的读取运行,是一个非常先进的方法。这个机制叫存储程序方式。
磁盘缓存(disk cache):内存的一部分作为磁盘,加快时间。
磁盘的程序读取到内存中要花费不少时间。把磁盘中读出来的数据保存到内存里,对于重复读取一些数据的情况能节省很多时间。
这是把低速设备的数据保存到高速设备中。
Web浏览器也是这样的。网络数据传输在面对大文件时速度慢,这时读取本地磁盘数据会更快。所以通过把网络数据存到本地磁盘效率会更高。
虚拟内存:磁盘的一部分作为内存,增大空间。
原理:将实际内存中暂时不需要的数据和磁盘的“假想内存”进行置换。
方法有两种思路:
- 分段式:把运行程序分割成以集合为单位的段落,将这些段落进行内存和磁盘间的置换。
- 分页式:将运行程序以页为单位进行置换。将需要运行的部分
Page in
, 不需要的部分Page out
磁盘的物理结构:
把磁盘的物理表面划分成多个空间,就能有所区分。
划分方式有两种:
- 扇区:固定长度空间
- 可变长:可变空间
Windows的硬盘软盘都是扇区。
磁盘表面分成若干个同心圆的空间就是磁道,把磁道按照固定大小划分成的空间就是扇区。
windows磁盘扇区一个是512byte
逻辑(软件)上,扇区读写单位是簇。
硬盘中,不同磁盘,1簇代表的扇区数目不同。软盘中,簇和扇区的容量相等。
一个文件最少占用1簇的空间。不同文件存放在1簇中会在空间操作上发生粘连。
这会不可避免的造成空间浪费,但减少簇容量,磁盘访问次数就会增加,读写时间变长,甚至整体容量也会相应减小。
这需要达到处理速度和存储容量的平衡。
[组原]《程序是怎样运行的》 笔记相关推荐
- IOS学习笔记05---C语言程序的开发运行过程
IOS学习笔记05---C语言程序的开发运行过程 0 5.C语言3-C语言程序的开发运行过程 ----------------------------------------------------- ...
- 使用组策略禁止域用户运行特定软件名称的程序
禁止用户运行特定程序 基于程序名称禁止培训部的用户运行计算器软件. 任务: u 设置组策略禁止培训部用户运行计算器软件 u 验证设置 步骤: 1. 在DCServer上,打开组策略管理工具,右击链接在 ...
- 【FastDDS学习笔记】HelloWorld示例程序编译和运行
目录 第一章:[FastDDS学习笔记]Ubuntu22上安装fastDDS环境 第二章:[FastDDS学习笔记]HelloWorld示例程序编译和运行 第三章:[FastDDS学习笔记]Fast- ...
- 飞龙的程序员书单 - 组原、OS、网络
组原 深入理解计算机系统 简单介绍一下,这本书包括组成原理和操作系统两大部分知识.第二.三章学完之后,逆向就算是入门了.国内的教材很少有拿汇编和C语言对比教学的书籍,这样的教学方法很实用.因为现在汇编 ...
- Java核心卷Ⅱ(原书第10版)笔记(上)
Java核心卷Ⅱ(原书第10版)笔记(上) 写在最前面,个人认为,卷Ⅱ更适合当手册使用,更多的是讲API的使用,前两章内容比较实际,要是合并到卷一就好了. 文章目录 第1章 Java SE 8 的流库 ...
- 【黑马程序员新版Linux学习笔记】Linux系统实用操作命令——操作演示
[黑马程序员新版Linux学习笔记]Linux 零基础快速入门: (一)Linux基本命令--操作演示 (二)Linux用户和权限 -- 操作演示 (三)Linux 小技巧快捷键 (四)Linux系统 ...
- [ULK11]信号(三):从信号传递到原程序恢复执行
背景:内核刚刚处理完中断和异常.现在它要返回到@current进程了.然而,在返回以前,内核总会习惯性检查一下@current的TIF_SIGPENDING标志,以便确定是否有尚未处理的信号.很不幸, ...
- surf程序 matlab,Matlab图像处理学习笔记(七):surf特征点
本文主要演示如何使用matlab自带的Computer Vision System Toolbox这个工具箱进行suft特征点的检测.匹配及显示.这个工具箱是matlab2012b及之后才有的一个工具 ...
- 黑马程序员_java自学学习笔记(八)----网络编程
黑马程序员_java自学学习笔记(八)----网络编程 android培训. java培训.期待与您交流! 网络编程对于很多的初学者来说,都是很向往的一种编程技能,但是很多的初学者却因为很长一段时间无 ...
最新文章
- 2022-2028年中国打印耗材市场现状调研与进展趋势分析报告
- RDKit:化合物骨架分析(基于Python3)
- Yii2 HOW-TO(2):最佳实践(1)
- C++知识点46——类继承中的类型转换与访问权限控制(中)
- Scott Hanselman's 推荐的的实用工具集合(2011版)
- html中投影效果图,利用CSS3(box shadow)制作边框投影
- gitosis使用笔记
- Gnuplot使用x11终端自动注销问题
- 2016.8.1今天是建军节
- 洛谷 P 4180 次小生成树
- linux 日志面试题,Linux运维 | 面试题
- 文字穿插在海报设计中的不同用法
- Flask cookie
- colmak键盘_萌神进化 IKBC 新POKER2机械键盘体验
- VMware安装Ubuntu 21.10
- 深入理解android(三):xposed详解,Xposed原理
- [Java]-单例模式与volatile简介
- MYSQL中AS(取别名)
- 使用手机软件Bluino Loader通过蓝牙编程、烧录Arduino
- c语言检测邮箱地址,C语言实现电子邮件地址验证程序