【计组】指令和运算1--《深入浅出计算机组成原理》(二)

news2025/2/27 2:58:28

一、计算机指令

1、指令

从软件工程师的角度来讲,CPU就是一个执行各种计算机指令(Instruction Code)的逻辑.。

这里的计算机指令,也可以叫做机器语言

不同发CPU支持的机器语言不同,如个人电脑用的是Intel的CPU,苹果手机用的是ARM的CPU,这两种CPU各自支持的语言就是两组不同的计算机指令集。

一个计算机程序,是由成千上万条指令组成的,但是CPU里不能一直放着所有指令,所以计算机程序平时是存储在存储器中的。这种程序指令存储在存储器里的计算机,我们就叫做存储程序型计算机(Stored-program Computer)(现代计算机出世之前,有一种插线板计算机,是不能存储程序的,工程师在一个布满了各种插口合插座的板子上,用不同的电线来连接不同的插口合插座,从而完成各种计算任务)

程序编译成汇编语言,再由编译器翻译成机器码,一条机器码,就是一条计算机指令。

不同的 CPU 有不同的指令集,也就对应着不同的汇编语言和不同的机器码

常见的指令可以分为五大类

  • 算术类指令:加减乘除
  • 数据传输类指令:给变量赋值,在内存里读写数据
  • 逻辑类指令:逻辑上的与或非
  • 条件分支类指令:if-else
  • 无条件跳转指令:函数调用

2、指令跳转

拿 Intel CPU 来说,里面差不多有几百亿个晶体管,我们先不管几百亿的晶体管的背后是怎么通过电路运转起来的,逻辑上,我们可以认为,CPU 其实就是由一堆寄存器组成的。而寄存器就是 CPU 内部,由多个触发器(Flip-Flop)或者锁存器(Latches)组成的简单电路(触发器和锁存器,其实就是两种不同原理的数字电路组成的逻辑门)。

N 个触发器或者锁存器,就可以组成一个 N 位(Bit)的寄存器,能够保存 N 位的数据,比方说, 64 位 Intel 服务器,寄存器就是 64 位的。

三种比较特殊的寄存器

通用寄存器既可以存放数据,又能存放地址。 

一个程序执行的时候,CPU 会根据 PC 寄存器里的地址,从内存里面把需要执行的指令读取到指令寄存器里面执行,然后根据指令长度自增,开始顺序读取下一条指令。可以看到,一个程序的一条条指令,在内存里面是连续保存的,也会一条条顺序加载。

除了简单地通过 PC 寄存器自增的方式顺序执行外,条件码寄存器会记录下当前执行指令的条件判断状态,然后通过跳转指令读取对应的条件码,修改 PC 寄存器内的下一条指令的地址,最终实现 if…else 以及 for/while 这样的程序控制流程。

二、链接和装载

1、ELF和静态链接

 Linux 下,可执行文件和目标文件所使用的都是一种叫 ELF(Execuatable and Linkable File Format)的文件格式,中文名字叫可执行与可链接文件格式,这里面不仅存放了编译成的汇编指令,还保留了很多别的数据。

ELF 文件格式把各种信息,分成一个一个的 Section 保存起来。ELF 有一个基本的文件头(File Header),用来表示这个文件的基本属性,比如是否是可执行文件,对应的 CPU、操作系统等等。除了这些基本属性之外,大部分程序还有这么一些 Section:

  • 首先是.text Section,也叫作代码段或者指令段(Code Section),用来保存程序的代码和指令;
  • 接着是.data Section,也叫作数据段(Data Section),用来保存程序里面设置好的初始化数据信息;
  • 然后就是.rel.text Secion,叫作重定位表(Relocation Table)。重定位表里,保留的是当前的文件里面,哪些跳转地址其实是我们不知道的;
  • 最后是.symtab Section,叫作符号表(Symbol Table)。符号表保留了我们所说的当前文件里面定义的函数名称和对应地址的地址簿。

链接器会扫描所有输入的目标文件,然后把所有符号表里的信息收集起来,构成一个全局的符号表。然后再根据重定位表,把所有不确定要跳转地址的代码,根据符号表里面存储的地址,进行一次修正。最后,把所有的目标文件的对应段进行一次合并,变成了最终的可执行代码。

Windows 的可执行文件格式是一种叫作 PE(Portable Executable Format)的文件格式。同样一个程序,在 Linux 下可以执行而在 Windows 下不能执行,一个非常重要的原因就是,两个操作系统下可执行文件的格式不一样。

如果有一个能够解析 PE 格式的装载器,就有可能在 Linux 下运行 Windows 程序。Linux 下著名的开源项目 Wine,就是通过兼容 PE 格式的装载器,使得我们能直接在 Linux 下运行 Windows 程序的。而现在微软的 Windows 里面也提供了 WSL,也就是 Windows Subsystem for Linux,可以解析和加载 ELF 格式的文件。

2、程序装载

在运行可执行文件的时候,其实是通过一个装载器,解析 ELF 或者 PE 格式的可执行文件。装载器会把对应的指令和数据加载到内存里面来,让 CPU 去执行。

装载需要满足两个要求:

第一,可执行程序加载后占用的内存空间应该是连续的。执行指令的时候,程序计数器是顺序地一条一条指令执行下去。这也就意味着,这一条条指令需要连续地存储在一起。

第二,我们需要同时加载很多个程序,并且不能让程序自己规定在内存中加载的位置。虽然编译出来的指令里已经有了对应的各种各样的内存地址,但是实际加载的时候,其实没有办法确保这个程序一定加载在哪一段内存地址上。因为现在的计算机通常会同时运行很多个程序,可能你想要的内存地址已经被其他加载了的程序占用了。

要满足这两个基本的要求,可以在内存里面,找到一段连续的内存空间,分配给装载的程序,然后把这段连续的内存空间地址和整个程序指令里指定的内存地址做一个映射。

指令里用到的内存地址叫作虚拟内存地址(Virtual Memory Address),实际在内存硬件里面的空间地址,叫物理内存地址(Physical Memory Address)。

我们维护一个虚拟内存到物理内存的映射表,这样实际程序指令执行的时候,会通过虚拟内存地址,找到对应的物理内存地址,然后执行。因为是连续的内存地址空间,所以我们只需要维护映射关系的起始地址和对应的空间大小就可以了。

2.1 内存分段

分段是找出一段连续的物理内存和虚拟内存地址进行映射的方法。,就是指系统分配出来的那个连续的内存空间。

分段的办法解决了程序本身不需要关心具体的物理内存地址的问题,但它也有一些不足之处,第一个就是内存碎片(Memory Fragmentation)的问题。

解决内存碎片的办法是内存交换(Memory Swapping):可以把 Python 程序占用的那 256MB 内存写到硬盘上,然后再从硬盘读回到紧跟着已经被占用了的 512MB 内存后面的内存里。

虚拟内存、分段,再加上内存交换,看起来似乎已经解决了计算机同时装载运行很多个程序的问题,但这三者的组合仍然会遇到一个性能瓶颈:硬盘的访问速度要比内存慢很多,而每一次内存交换,都需要把一大段连续的内存数据写到硬盘上。所以,如果内存交换的时候,交换的是一个很占内存空间的程序,这样整个机器都会显得卡顿。

2.2 内存分页

内存分页(Paging)可以让内存交换的时候,需要交换写入或者从磁盘装载的数据更少一点,以解决内存交换存在的问题。

和分段这样分配一整段连续的空间给到程序相比,分页是把整个物理内存空间切成一段段固定尺寸的大小。而对应的程序所需要占用的虚拟内存空间,也会同样切成一段段固定尺寸的大小。这样一个连续并且尺寸固定的内存空间,我们叫(Page)。

从虚拟内存到物理内存的映射,不再是拿整段连续的内存的物理地址,而是按照一个一个页来的。页的尺寸一般远远小于整个程序的大小。在 Linux 下,通常只设置成 4KB。

由于内存空间都是预先划分好的,就没有了不能使用的碎片,只有被释放出来的很多 4KB 的页。即使内存空间不够,需要让现有的、正在运行的其他程序,通过内存交换释放出一些内存的页出来,一次性写入磁盘的也只有少数的一个页或者几个页,不会花太多时间,让整个机器被内存交换的过程给卡住。

更进一步地,分页的方式使得我们在加载程序的时候,不再需要一次性都把程序加载到物理内存中。我们完全可以在进行虚拟内存和物理内存的页之间的映射之后,只在程序运行中,加载当前用到的页。

3、动态链接

程序的静态链接,是把对应的不同文件内的代码段,合并到一起,成为最后的可执行文件。链接的方式,让代码做到了“复用”,同样的功能代码只要写一次,然后提供给很多不同的程序进行链接就行了。

但是,如果有很多个程序都要通过装载器装载到内存里面,那里面链接好的同样的功能代码,也都需要再装载一遍,再占一遍内存空间。

动态链接的过程中,我们想要“链接”的,不是存储在硬盘上的目标文件代码,而是加载到内存中的共享库(Shared Libraries)

这个共享库会被很多个程序的指令调用到。在 Windows 下,这些共享库文件就是.dll 文件,也就是 Dynamic-Link Libary(DLL,动态链接库)。在 Linux 下,这些共享库文件就是.so 文件,也就是 Shared Object(一般我们也称之为动态链接库)

要想要在程序运行的时候共享代码,编译出来的共享库文件的指令代码,必须是地址无关码(Position-Independent Code)。换句话说就是,这段代码,无论加载在哪个内存地址,都能够正常执行。

在动态链接对应的共享库的 data section 里面,保存了一张全局偏移表(GOT,Global Offset Table)。虽然共享库的代码部分的物理内存是共享的,但是数据部分是各个动态链接它的应用程序里面各加载一份的。所有需要引用当前共享库外部地址的指令,都会查询 GOT,来找到当前运行程序的虚拟内存里的对应位置。而 GOT 表里的数据,则是在加载共享库的时候写进去的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/44859.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

同样Java后端开发三年,朋友已经涨薪到了30k,而我才刚到12K。必须承认多背背八股文确实有奇效!

程序猿在世人眼里已经成为高薪、为人忠诚的代名词。 然而,小编要说的是,不是所有的程序员工资都是一样的。 世人所不知的是同为程序猿,薪资的差别还是很大的。 众所周知,目前互联网行业是众多行业中薪资待遇最好的,…

2022年NPDP新版教材知识集锦--【第四章节】(2)

【概念设计阶段】(全部获取文末) 概念描述提供了产品概念的优点和特征的定性描述,其必要性体现在: ①为开发团队的所有成员以及与项目相关的成员提供了清晰性和一致性。 ②是向潜在客户解释产品的重要手段之一。 典型流程: 2.1概念工程 …

python使用websocket服务并在fastAPI中启动websocket服务

依赖 pip install websockets-routes 代码 import asyncio import websockets import websockets_routes from websockets.legacy.server import WebSocketServerProtocol from websockets_routes import RoutedPath# 初始化一个router对象 router websockets_routes.Router()…

Archlinux安装软件的那些事

个人主页:董哥聊技术我是董哥,嵌入式领域新星创作者创作理念:专注分享高质量嵌入式文章,让大家读有所得!文章目录1、ArchLinux1.1 ArchLinux原则1.2 软件包管理1.2.1 软件仓库1.2.2 包管理器2、Pacman2.1 pacman介绍2.…

什么是幂等性?四种接口幂等性方案详解!

幂等性在我们的工作中无处不在,无论是支付场景还是下订单等核心场景都会涉及,也是分布式系统最常遇到的问题,除此之外,也是大厂面试的重灾区。 知道了幂等性的重要性,下面我就详细介绍幂等性以及具体的解决方案&#…

SpringBoot中自动配置

第一种: 给容器中的组件加上 ConfigurationProperties注解即可 测试: Component ConfigurationProperties(prefix "mycar") public class Car {private String brand;private Integer price;private Integer seatNum;public Integer getSeat…

币圈已死,绿色积分是全新的赛道吗?

近几年来,移动互联网行业的迅猛发展,快速改变着社会业态。尽管如此,仍有大量企业线上线下处于割裂状态,2020 年一场疫情的突然爆发,并持续到 2022年,对零售行业造成流量崩塌、供应链中断、市场供需下滑等压…

现代 CSS 高阶技巧,完美的波浪进度条效果。

将专注于实现复杂布局,兼容设备差异,制作酷炫动画,制作复杂交互,提升可访问性及构建奇思妙想效果等方面的内容。 在兼顾基础概述的同时,注重对技巧的挖掘,结合实际进行运用,欢迎大家关注。 正…

金属非金属如何去毛刺 机器人浮动去毛刺

毛刺的产生 在金属非金属零件的加工中,由于切削加工过程中塑性变形引起的毛边,或者是铸造、模锻等加工的飞边,或是焊接挤出的残料,这些与所要求的形状、尺寸有所出入,在被加工零件上派生出的多余部分即为毛刺&#xf…

音视频开发之 ALSA实战!

前言: 今天我们来分享一个开源的音频采集代码,现在大部分音频采集都是通过ALSA框架去采集,如果大家把ALSA采集代码学懂,那么大部分的音频采集都可以搞定。这个代码是用ALSA进行音频PCM的采集并保存到本地文件。一、alsa框架的介绍…

C#语言实例源码系列-实现输入框焦点变色和窗体拖拽改变大小

专栏分享点击跳转>Unity3D特效百例点击跳转>案例项目实战源码点击跳转>游戏脚本-辅助自动化点击跳转>Android控件全解手册 👉关于作者 众所周知,人生是一个漫长的流程,不断克服困难,不断反思前进的过程。在这个过程中…

002.组合总和|||——回溯算法

1.题目链接: 216. 组合总和 III 2.解题思路: 2.1.题目要求: 给一个元素数量k和一个元素和n,要求从范围[1,2,3,4,5,6,7,8,9]中返回所有元素数量为k和元素和为n的组合。(每个数字只能使用一次) 比如输入k…

深度学习快速入门----Pytorch 系列2

注:参考B站‘小土堆’视频教程 视频链接:【PyTorch深度学习快速入门教程(绝对通俗易懂!)【小土堆】 上一篇:深度学习快速入门----Pytorch 1 文章目录八、神经网络--非线性激活九、神经网络--线性层及其他层…

作为IT行业过来人,我有3个重要建议给后辈程序员!

见字如面,我是军哥!作为一名 40 岁的 IT 老兵,我在年轻时踩了不少坑,我总结了其中最重要的 3 个并一次性分享给你,文章不长,你一定要看完哈~1、重视基础还不够,还要注重技术广度和深…

第2-4-8章 规则引擎Drools实战(1)-个人所得税计算器

文章目录9. Drools实战9.1 个人所得税计算器9.1.1 名词解释9.1.2 计算规则9.1.2.1 新税制主要有哪些变化?9.1.2.2 资较高人员本次个税较少,可能到年底扣税增加?9.1.2.3 关于年度汇算清缴9.1.2.4 个人所得税预扣率表(居民个人工资、…

科教导刊杂志科教导刊杂志社科教导刊编辑部2022年第27期目录

前沿视角《科教导刊》投稿:cn7kantougao163.com 新时代研究生教育质量评价指标体系的框架构建 李军伟;赵永克;杨丹; 1-3 基于现代学徒制的“多主体、双标准、五维度”人才培养质量评价体系构建 汪帆;刘严; 4-6 高教论坛 新工科背景下地方性院校第二课堂…

【云原生】Docker容器服务更新与发现之consul

内容预知 1.consul的相关知识 1.1 什么是注册与发现 1.2 什么是consul 1.3 zookeeper和consul的区别 2. consul 部署 2.1 部署consul服务器 2.2 registrator服务器 3.consul-template 的引入 3.1 consul-template的作用 3.2 consul-template的具体部署运用 &…

微信开发者工具C盘占用大的问题

将User Data 下的文件迁移到其他盘,比如 D盘,E盘,F盘 步骤如下: 1.找到微信开发者工具C盘所在的缓存目录,一般为 C:\Users\ 你的用户名\AppData\Local\微信开发者工具\User Data 将里面的内容全部剪切到其它盘符&…

从鹅厂实例出发!分析Go Channel底层原理

本文是基于Go1.18.1源码的学习笔记。Channel的底层源码从Go1.14到现在的Go1.19之间几乎没有变化,这也是Go最早引入的组件之一,体现了Go并发思想:Do not communicate by sharing memory; instead, share memory by communicating.不要通过共享…

Playwright 简明入门教程:录制自动化测试用例,结合 Docker 使用

本篇文章聊聊如何使用 Playwright 进行测试用例的录制生成,以及如何在Docker 容器运行测试用例,或许是网上最简单的入门教程。 写在前面 Playwright 是微软出品的 Web 自动化测试工具和框架,和 Google Puppeteer 有着千丝万缕的关系。前一阵…