当前位置:首页 > 公众号精选 > IOT物联网小镇
[导读]【Linux从头学】是什么古老的Intel8086处理器主存储器是什么?寄存器是什么?三个总线CPU如何对内存进行寻址?我们是如何控制CPU的?CPU执行指令流程【Linux从头学】是什么这两年多以来,我的本职工作重心一直是在x86Linux系统这一块,从驱动到中间层,再到应用层...


  • 【Linux 从头学】是什么


  • 古老的 Intel8086 处理器


  • 主存储器是什么?


  • 寄存器是什么?


  • 三个总线


  • CPU 如何对内存进行寻址?


  • 我们是如何控制 CPU 的?


  • CPU 执行指令流程


【Linux 从头学】是什么

这两年多以来,我的本职工作重心一直是在 x86 Linux 系统这一块,从驱动到中间层,再到应用层的开发。


随着内容的不断扩展,越发觉得之前很多基础的东西都差不多忘记了,比如下面这张表(《深入理解 LINUX 内核》第47页):


Linux 从头学 01:CPU 是如何执行一条指令的?这张表描述了Linux系统中几个段描述符信息。


数据段代码段,仔细看一下相关书籍就知道这些描述符代表什么意思,但是:


为什么这几个段的 Base 地址都是0x00000000?


为什么 Limit 都是0xfffff?


为什么它们的 Type 类型和优先级 DPL 又各不相同?


如果没有对x86平台的一些基础知识的理解,要啃完这本书真的是挺费力气的!


更要命的是,随着Linux内核代码的体积不断膨胀,最新的 5.13 版本压缩档已经是一百多兆了:


Linux 从头学 01:CPU 是如何执行一条指令的?这么一个庞然大物,如何下手才能真正的学好Linux呢?!


即便是从 Linux 0.11 版本开始,其中的很多代码看起来也是非常费劲的!


周末在整理一些吃灰的书籍时,发现几本以前看过的好书: 王爽的《汇编语言》,李忠的《从实模式到保护模式》,马朝晖翻译的《汇编语言程序设计》等等。


都是非常-非常-老的书籍,再次翻了一下,真心觉得内容写得真好


对一些概念、原理、设计思路的描述,清晰而透彻。


Linux系统中的很多关于分段、内存、寄存器相关的设计,都可以在这些书籍中找到基础支撑。


于是乎,我就有了一个想法:是否可以把这些书籍中,与Linux系统相关的内容进行一次重读和整理,但绝不是简单的知识搬运。


考虑了一下,大概有下面几个想法:


  1. 先确定最终目标的目标:学习 Linux 操作系统;


  2. 这几本书写的都是汇编语言,以及比较基础的底层知识。我们会淡化汇编语言部分,把重点放在与 Linux 操作系统有关联的原理部分;


  3. 不会严格按照书中的内容、顺序来输出文章,而是把几本书中内容相关的部分放在一起学习、讨论;


  4. 有些内容,可以与 Linux 2.6 版本中的相关部分进行对比分析,这样的话在以后学习 Linux 内核部分时,可以找到底层的支撑;


  5. 最后,希望我自己能坚持这个系列,也算是给自己的一个梳理吧。


一句话:以基础知识为主!


作为开篇第一章,本文将会描述下面这张图的执行步骤:


Linux 从头学 01:CPU 是如何执行一条指令的?现在就开始吧!


古老的 Intel8086 处理器

8086是Intel公司的第一款16位处理器,诞生于1978年,应该比各位小伙伴的年龄都大一些。


在Intel公司的所有处理器中,它占有很重要的地位,是整个Intel 32位架构处理器(IA-32)的开山鼻祖


那么,问题来了,什么叫 16 位的处理器?


有些人会把处理器的位数与地址总线的位数搞混在一起!


我们知道,CPU在访问内存的时候,是通过地址总线来传送物理地址的。


8086 CPU有20位的地址线,可以传送20位地址。


每一根地址线都表示一个bit,那么20个bit可以表示的最大值就是 2 的 20 次方


也就是说:最大可以定位到1M地址的内存,这称作CPU的寻址能力


但是,8086处理器却是16位的,因为:


  1. 运算器一次最多可以处理 16 位的数据;


  2. 寄存器的最大宽度为 16 位;


  3. 寄存器和运算器之间的通路为 16 位;


也就是说:在8086处理器的内部,能够一次性处理、传输、暂时存储的最大长度是16位,因此,我们说它是 16 位结构的 CPU


主存储器是什么?

计算机的本质就是对数据的存储和处理,那么参与计算的数据是从哪里来的呢?那就是一个称作 存储器(Storage 或 Memory)的物理器件。


从广义上来说,只要能存储数据的器件都可以称作存储器,比如:硬盘、U盘等。


但是,在计算机内部,有一种专门与CPU相连接,用来存储正在执行的程序和数据的存储器,一般称作内存储器或者主存储器,简称:内存或主存


内存按照字节来组织,单次访问的最小单位是1个字节,这是最基本的存储单元


每一个存储单元,也就是一个字节,都对应着一个地址,如下图所示:


Linux 从头学 01:CPU 是如何执行一条指令的?CPU就通过地址总线来确定:对内存中的哪一个存储单元中的数据进行访问。


第 1 个字节的地址是 0000H,第 2 个字节的地址是 0001H,后面以此类推。


图中的这个内存,最大存储单元的地址是FFFFH,换算成十进制就是65535,因此这个内存的容量是65536字节,也就是64 KB。


这里有一个原子操作的问题可以考虑一下。


在Linux内核代码中,很多地方使用了原子操作,比如:互斥锁的实现代码。


为什么原子操作需要对变量的类型限制为int型呢?这就涉及到对内存的读写操作了。


尽管内存的最小组成单位是字节,但是,经过精心的设计和安排,不同位数的CPU,能够按照字节、字、双字进行访问。


换句话说,仅通过单次访问,16位处理器就能处理16位的二进制数,32位处理器就能处理32位的二进制数。


寄存器是什么?

在CPU内部,一些都是代表 0 或 1 的电信号,这些二进制数字的一组电信号出现在处理器内部线路上,它们是一排高低电平的组合,代表着二进制数中的每一位。


在处理器内部,必须用一个称为寄存器的电路把这些数据锁存起来。


因此,寄存器本质上也属于存储器的一种。只不过它们位于处理器的内部,CPU访问寄存器比访问内存的速度更快。


处理器总是很忙的,在它操作的过程中,所有数据在寄存器里面只能是临时存在一小会,然后再被送往别处,这就是为什么它被叫做“寄存器”


Linux 从头学 01:CPU 是如何执行一条指令的?8086中的寄存器都是16位的,可以存放2个字节,或者说1个字。字节在前(bit8 ~ bit15),字节在后(bit0 ~ bit7)。


8086中有下面这些寄存器:


Linux 从头学 01:CPU 是如何执行一条指令的?刚才说了,这些寄存器都是16位的。由于需要与以前更古老的处理器兼容,其中的4个寄存器:AX、BX、CX、DX 还可以当成 2 个 8 位的寄存器来使用。


比如:AX代表一个16位的寄存器,AH、AL分别代表一个8位的寄存器。


mov AX, 5D 表示把 005D 送入 AX 寄存器(16 位)
mov AL, 5D 表示把 5D 送入 AL 寄存器(8 位)

三个总线

当我们启动一个应用程序的时候,这个程序的代码和数据都被加载到物理内存中。


CPU无论是读取指令,还是操作数据,都需要与内存进行信息的交互:


  1. 确定存储单元的地址(地址信息);


  2. 器件的选择,读或写的命令(控制信息);


  3. 读或写的数据(数据信息);


在计算机中,有专门连接CPU和其他芯片的数据,称为总线


从逻辑上来分类,包括下面3种总线:


地址总线:用来确定存储单元的地址;
控制总线: CPU 对外部器件进行控制;
数据总线: CPU 与内存或其他器件之间传送数据;


Linux 从头学 01:CPU 是如何执行一条指令的?8086 有20根地址线,称作地址总线的宽度,它可以寻址 2 的 20 次方个内存单元。


同样的道理,8086 数据总线的宽度是16,也就是一次性可以传送16 bit的数据。


控制总线决定了CPU可以对外进行多少种控制,决定了CPU对外部器件的控制能力


CPU 如何对内存进行寻址?

在Linux 2.6内核代码中,编译器产生的地址叫做虚拟地址(也称作:逻辑地址),这个逻辑地址经过段转换之后,变成线性地址,线性地址再经过分页转换,就得到最终物理内存上的物理地址


Linux 从头学 01:CPU 是如何执行一条指令的?还记得文章开头的那张段描述符的表格吗?


其中的代码段和数据段描述符的起始地址都是0x00000000,也就是说: 在数值上虚拟地址和转换后的线性地址是相等的(稍后就会明白为什么是这样)。


我们再来看看一下8086中更简单的地址转换。


刚才说到,内存是一个线性的存储器件,CPU依赖地址来定位每一个存储单元。


对于8086 CPU来说,它有20根地址线,可以传送20位地址,达到1MB的寻址能力。


但是8086又是16位的结构,在内部一次性处理、传输、暂时存储的地址只有 16 位


从内部结构来看,如果将地址从内部简单的发出到地址总线上,只能送出16位的地址,这样的话,寻址能力只有64KB。


那么应该怎么才能充分利用20根地址线呢?


8086 CPU采用: 在内部使用两个 16 位地址合成的方法,来形成一个20位的物理地址,如下所示:


Linux 从头学 01:CPU 是如何执行一条指令的?第一个16位的地址称为段地址,第二个16位的地址称为偏移地址


地址加法器采用下面的这个公式,来“合成”得到一个20位的物理地址


物理地址 = 段地址 x 16 偏移地址


例如:我们编写的程序,在加载到内存中之后,放在一个内存空间中。


CPU 在执行这些指令的时候,把CS寄存器当做寄存器,把IP寄存器当做偏移寄存器,然后计算 CS x 16 IP 的值,就得到了指令的物理地址


从以上的描述中可以看出:8086 CPU 似乎是因为寄存器无法直接输出20位的物理地址,不得已才使用这样的地址合成方式。


其实更本质的原因是:8086 CPU 就是想通过 基地址 偏移量 的方式来对内存进行寻址(这里的基地址,就是段地址左移 4 位)。


也就是说,即使CPU有能力直接输出一个20位的地址,它仍然可能会采用 基地址 偏移量的方式来进行内存寻址。


想一下:我们在Linux系统中编译一个库文件的时候,一般都会在编译选项中添加-fPIC选项,表示编译出来的动态库是地址无关的,在被加载到内存时需要被重定位。


而基地址 偏移量的寻址模式,就为重定位提供了底层支撑


我们是如何控制 CPU 的?

CPU其实是一个很纯粹、很呆板的一个东西,它唯一做的事情就是:到 CS:IP 这两个寄存器指定的内存单元中取出一条指令,然后执行这条指令:


Linux 从头学 01:CPU 是如何执行一条指令的?当然了,还需要预先定义一套指令集,在内存中的指令区中,存储的都必须是合法的指令,否则 CPU 就不认识了。


每一条指令都是用某些特定的数(指令码)来指示CPU进行特定的操作。


CPU认识这些指令,一看到这些指令码,CPU就知道这个指令码后面还有几个字节的操作数、需要进行什么样的操作。


例如:指令码F4H 表示让处理器停机,当CPU执行这条指令的时候,就停止工作。


(其实这里说CPU已经有点不准确了,因为 CPU 是囊括了很多器件的一个整体,也许这里说CPU中的执行单元会更准确些。)


另外有一点可以提前说一下:内存中的一切都是数据,至于把其中的哪一部分数据当做指令来执行,哪一部分数据当做被指令操作的“变量”,这完全是由操作系统的设计者来规划的。


在 8086 处理器的层面来说,只要是 CS:IP “指向”的内存区域,都被当做指令来执行。


从以上描述可以看出:在CPU中,程序员能够用指令读写的器件只有寄存器,我们可以通过改变寄存器中的内容,来实现对 CPU 的控制。


更直白的说就是:我们可以通过改变 CS、IP 寄存器中的内容,来控制CPU执行目标指令。


作为一名合格的嵌入式开发者,大家估计都配置过一些单片机里的寄存器,以达到一些功能定义、端口复用的目的,其实这些操作,都可以看做是我们对 CPU 的控制。


如果把 CPU 比作木偶,那么 寄存器就是控制木偶的绳索


我们再把CPU与 工控领域的PLC编程进行类比一下。


我们在拿到一个新的PLC设备之后,其中只有一个运行时(runtime),这个运行时执行的本职工作就是:


  1. 扫描所有的输入端口,锁存在输入映象区;


  2. 执行一个运算、控制逻辑,得到一些列输出信号,锁存到输出映象区;


  3. 把输出映象区的信号,刷新到输出端口;


Linux 从头学 01:CPU 是如何执行一条指令的?在一个全新的 PLC 中,其中第 2 个步骤中需要的运算、控制逻辑可能就不存在。


因此,单单一个runtime,PLC是无法完成一件有意义的工作的。


为了让PLC完成一个具体的控制目标,我们还需要利用PLC厂家提供的上位机编程软件,开发一个运算、控制逻辑程序,编程语言一般都是梯形图居多。


当这个程序被下载到PLC中之后,它就可以控制运行时来做一些有意义的工作了。


我们可以简单的认为:梯形图就是用来控制 PLC 的运行时


Linux 从头学 01:CPU 是如何执行一条指令的?对于CPU来说,想让它执行某个内存单元的指令,只要修改寄存器CS和IP即可。


换句话说:只要对一个程序的内存布局足够的清楚,可以把 CPU 玩弄于股掌之间,让它执行哪里的代码都可以。


CPU 执行指令流程

现在我们已经明白了地址转换、内存的寻址,距离CPU执行一条指令需要的最小单元还剩下:指令缓冲区和控制电路


简单来说:指令缓冲区用来缓存从内存中读取的指令,控制电路用来协调各种器件对总线等资源的使用。


对于下面这张图来说,它一共有4条指令:


Linux 从头学 01:CPU 是如何执行一条指令的?第一条指令来举例,它一共经过5个步骤:


  1. 把 CS:IP 内容送入地址加法器,计算得到 20 位的物理地址 20000H;


  2. 控制电路把 20 位的地址,送入到地址总线;


  3. 内存中 20000H 单元处的指令 B8 23 01,经过数据总线被送到指令缓冲区;


  4. 指令偏移寄存器 IP 的值要加 3,指向下一条等待被执行的偏移地址(因为指令码 B8 代表当前指令的长度是 3 个字节);


  5. 执行指令缓冲区中的指令: 把数值 0123H 送入寄存器 AX 中;


以上就是一条指令的执行最基本步骤,当然,现代处理器的指令执行流程,比这里的要复杂的多得多。



------ End ------
万丈高楼平地起!


这篇文章,仅仅描述了CPU执行一条指令所需要的最小知识点。


下一篇文章,我们再继续对内存的分段机制进行更进一步的窥探。


本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

关键字: 阿维塔 塞力斯 华为

加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

关键字: AWS AN BSP 数字化

伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

关键字: 汽车 人工智能 智能驱动 BSP

北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

关键字: 亚马逊 解密 控制平面 BSP

8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

关键字: 腾讯 编码器 CPU

8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

关键字: 华为 12nm EDA 半导体

8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

关键字: 华为 12nm 手机 卫星通信

要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

关键字: 通信 BSP 电信运营商 数字经济

北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

关键字: VI 传输协议 音频 BSP

北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

关键字: BSP 信息技术
关闭
关闭