计算机
2022-05-11 14:41:31 44 举报
计算机是一种能够快速、准确和高效处理数据的电子设备。它由硬件和软件两部分组成,硬件包括中央处理器(CPU)、内存、硬盘驱动器、显示器等物理组件,而软件则包括操作系统、应用程序等可执行的程序。计算机可以执行各种任务,如文字处理、数据分析、图形设计、游戏娱乐等。它的应用领域广泛,涵盖了科学、教育、医疗、商业等各个领域。随着技术的不断发展,计算机的性能不断提高,功能也越来越强大。如今,计算机已经成为人们生活和工作中不可或缺的一部分。
作者其他创作
大纲/内容
研究一门语言怎么转成另外一门语言
LLVM IR
GDTR
指令地址寄存器
加载
栈段1描述符
Berkeley Packet Filter
性能下降
Python AST
计算机体系结构
LLVM 后端
程序由一组代码组成(指令流)
地址3
Head.S
定义语法
这里讨论的是32位机
目标语言
D\\B
GraalVM
LLVM 集成汇编器
地址寄存器16位
代码段1描述符
寄存器组
采用二进制作为计算机数值计算的基础以0、1代表数值。组成:运算器、存储器、控制器、输入输出
1B
字节码
大
2位表示RPL程序的特权级
汇编器按照ISA规则放置0和1
代码段内存
4G
汇编器定义的基本操作:mov(移动,寄存器和内存),sub,add,算术运算,逻辑运算(与或非),移位等等
1告诉控制单元,2去哪个寄存器单元读取什么数据,3放到运算单元做什么运算,4运算结果写回寄存器单元
类型
0-15表示段界限Segment Limit
GCC
C语言最纯粹的汇编抽象,没有添加语言自身的特性
高速缓存
IR(IR是class字节码)
8086物理地址=段寄存器 左移(二进制4位,十六进制1位)+指令地址寄存器例如:段寄存器为:0x 1200,指令地址寄存器为:0x 0121物理地址=(0x 1200 <<1)+0x 0121=0x12121
指令4
语法分析
用了llvm的后端
单词是表示信息的最小单位
解释器
CS段寄存器
前端
对着ISA指令集写机器码
应用程序A
计算机
Ruby
高地址:H
提供了保护模式真32位机(寄存器,地址总线都是32位)虚拟内存(基于段)特权级内存模型指令流水线1级缓存X87FPU
4表示类型TYPE
JVM适配各个操作系统
流程控制语句
由操作系统控制
GCC链接器或LLD(开发中)
0x FFFF
指令跳转分为:指令段内指令的状态机跳转(if语句,循环语句),指令段指令的跳转(方法调用)
源程序
得出
内存
指令5
数据内存
搭配使用,用于开辟栈帧
8表示段基地址Base Address
这三个编译完就是class字节码(IR)
操作数
计算机组成原理
程序
二进制程序
GDT表的首地址
数论
最低特权级
查表
指令段A私有数据A的栈帧
栈内存
汇编代码movaddsubmuldiv
Java Hostspot VM
JavaScriptInterpreter
位数决定了地址总数。2^n
通用段寄存器通用S
语义分析
怎么写指令?怎么读?写好的指令怎么存?
指令1
指令3
Java、Kotlin、Scala
数据总线
先存硬盘,程序运行加载到内存
fork会返回2次0号进程不会进行init()而是执行pause()fork出来的1号进程执行init
..
根据图灵机(有限状态机)得出还需要定义跳转指令,根据指令执行状态进行跳转-->需要有状态寄存器存状态
3G
不使用VM,需要接受全部数据后,浏览器端将不要的数据丢弃。传输不要的数据会导致性能下降
指令段A
1位
ISA指令集
为什么需要提供保护模式CUP通过指令地址寄存器和段寄存器寻址,如果CPU不采取任何策略,那就意味着任何一个程序通过修改指令地址寄存器和段寄存器就可以访问、修改内存中其他程序或者操作系统的内存,必然会出问题。因为CPU控制计算机,OS控制CPU,那么程序修改OS的内存等同于程序控制OS,等同于控制了整台计算机。如何对内存中的程序提供保护功能?最简单的做法就是隔离,将每个程序隔离1.使用虚拟地址,程序认为自己独占整块内存,由操作系统和CPU控制程序的虚拟内存到物理内存的映射。2.为了防止程序控制OS,使用特权级保护OS,让OS运行在最高特权级,程序运行在最低特权级,程序无法修改自己的特权级,不同特权级之间的访问需要进行特权级校验。特权级用0到3表示。Linux只用了0和3(二进制00和11,00最高,11最低)。
call _copy_process
共享数据
GDT Item
...
Hotspot面向接口编程,JIT的实现可以是Graal、C1、C2
一个后进先出数据结构
问题1:程序的特权级保存在哪里?段寄存器问题2:在访问的时候需要有一个特权级校验,这个校验位放在那里?GDT表的段描述符里
计算机硬件
段寄存器
相等
热点代码
一片内存OS专享
控制器
数据结构
Kotlin
A调B
四者关系
数据3
汇编器
4
数字逻辑是研究计算机硬件的基础基于数论和电学研究电气特性、逻辑门、触发器、锁存器、晶体管的实现原理
决定了GDT有多少Item2^13=8192个
0x 0000
汇编怎么操作寄存器组?
栈
触发器
数据B
描述存储数据的结构
栈帧A
最小寻址单元1byte
系统调用协议int 0x80sysenter
汇编就是操作寄存器组
存
C、C++、Rust
Compiler-RT运行时库
H
0G
A栈底的地址
堆
汇编语言就是对ISA的编码
电压电流(电气特性)
OS内存最高特权级
汇编
传输读\\写数据
JS AST
在内存中如何分布?
DRAM
将人们使用纸笔进行数学运算的过程进行抽象,由一个虚拟的机器替代人类进行数学运算。(我输入一段代码,给我输出结果,代码就是程序,结果就是机器停机后纸带上的数据)状态机组成:1、一条分格无限长的纸带,每格容纳一个字符2、一个读写头,可以在纸带上来回移动,可以读写纸带上格子的内容,可以改变自己的内部状态3、程序(一组控制读写头移动、读写、改变内部状态的规则)
操作系统需要控制CPU,控制了CPU=控制了一切
锁存器
屏蔽对内存单元大小的访问例如:定义byte是1字节,int是4字节等
S
给地址进行编号
0x FF FF FF FF
转换
一切语言的特性均面向编译器来设计和编写
中间语言VM通过执行IR选择需要的数据进行传输
图灵机模型
类似
因为CPU是控制器,所以控制了CPU就相当于控制了整台计算机。如何控制CPU?CPU厂商需要提供一堆01二进制的组合,来告诉用户怎么操作CPU。这堆组合称为指令集架构(ISA)
每个指令段都有自己的栈帧,栈帧里有当前指令段操作的数据,使用call指令转移指令段之后,只需要保留上一个指令段栈帧的栈底位置即可
抽象对数据的操作(例如add,sub等)例如:定义语言的符号+、-、*、/、=等
IR
1byte
程序2虚拟内存
JIT新的实现
Item大小64位=8B
屏蔽CPU底层处理,让编程更符合人类思维模式。而不是需要详细了解CPU的构建和计算机组成与体系结构。抽象汇编语言对CPU和内存的操作
通用寄存器:能用于它所表明的用途还能随便用
AST
外设
程序1
Java
基于跨平台的Hotspot JVM上层的中间表示(class字节码),让各类语言都可以在JVM上运行,可以享有JVM的高性能、GC、跨平台等等特性整合其他语言,统一虚拟机平台方便开发者实现一门新的语言,新语言只需要关注前端(词法分析、语法分析、语义分析)的实现,后端可以复用
实现了编译原理前端和后端的整套流程,只不过在内存中动态对语言进行编译执行速度快(对源代码进行了高度优化),但是启动速度慢(需要花费大量时间对源代码进行编译)编译需要消耗时间和空间不是所有的代码都需要编译,热点代码才需要编译
写在汇编里,静态的(在汇编的.data里定义)
一条指令就完成一条指令工作压力在编译器,编译器需要生成多条机器码
最终都是ISA
数据结构定义了数据存放的形式算法实现了操作这些数据的动作
添加语言自身的特性
数字逻辑
存到
用于指明控制的是哪个设备,控制的是读还是写
数据1
寄存器单元MU
JVM(JIT+解释器)
实现了编译原理前端和后端的整套流程,将源语言编译为目标语言
G
位数决定了一次传输n位数据
使用晶体管实现速度比DRAM快
程序内存分布
数据A
0x 00 00 00 00
逻辑门
为什么不用十进制?为了方便实现计算机的数值运算推理:1、计算机底层是数学(数论)和物理(电学)2、十进制需要十个状态,找到一个具有十个稳定状态的电子器件很困难的,计算机状态数越少越好。3、根据香农公式计算机应该用e进制,无法表示小数,只能向上取整或者向下取整,也就是使用2进制或3进制。目前的半导体设备不适合三进制
中间表示
Clang前端或带DragonEgg的GCC
养料
静态分配
为什么不用这种方法,直接生成字节码?我用c语言就想要c的高性能,不想要JIT,gc等降低我的性能Graal Compiler可以不依赖于Hostspot,直接根据AST编译为机器码,可以保留其他语言的特性
栈帧B
根据指令执行状态进行跳转
跳转指令读取状态寄存器进行跳转
cpu需要一个指向当前执行的指令地址的寄存器--指令地址寄存器
DPL
缓存
CPU
栈底寄存器BP
数据2
冯诺依曼明确提出了计算机的体系结构计算机一切由0、1组成0和1类似于易经的阴阳,目的都是用来表示信息
Scala
最终都会变成程序
LLVM IR优化器
为了保护OS引入特权级R0、R1、R2、R3。Linux只用了R0和R3,所有只关注R0和R3
将前端编译的一个个文件链接成大的文件交给优化器优化
栈帧C
三明治架构
网卡信息
Intel历史
晶体管
机器码0011 0010 0101 00100010 0010 0001 00100000 0010 0001 0010
宏定义解开#define __NR_fork 2int fork(void) { long __res; __asm__ volatile (\"int $0x80\" : \"=a\" (__res) : \"0\" (2)); if (__res >= 0) return (type) __res; errno = -__res; return -1; }fork 通过80中断调用sys_fork
词法分析
Intel编码
数据段2描述符
libc++标准库
取
怎么存,格式是什么?
GDT表放在内存中
怎么让其他指令流共享这个地址?在数据内存里,定义一个变量用来保存这个地址
Intel平台
有时候,希望有些数据初始化的值为一个无效值Object o= nullnull就指向这片内存
Graal Compiler
数据段寄存器DS
服务器
状态寄存器
P
与计算机体系挂钩,研究计算机硬件之间的组合交互行为
基本原理
系统库
16位表示段基地址Base Address
表示用GDT还是LDT
指令n
call指令
用一门语言对其他语言进行解释,执行逻辑例如:用c++解释执行class字节码,用汇编解释执行class字节码也是可以的程序启动,立即执行,省去编译时间,但是执行速度慢
Windows
OS内存
物理内存
Python
LLVMInterpreter
段264KB
操作码
main{ ...if (!fork()) { init(); }for(;;) pause();}
汇编按照汇编器定义的规则编写
2位DPL
ISA
适配各个指令集
分4卷,总分结构1和3卷重点看2和4卷用于查
例如:Hotspot源码里的bytecodeInterpreter.cpp用switch case解释执行字节码
程序2
指令地址寄存器IP
指令2
低地址:L
16进制编码
得出编译器设计
JVM CI(JVM Compiler Interface)
内存一定有地址
8086CPU 控制总线16位,地址总线20位,数据总线16位,寄存器16位CPU通过系统总线和内存进行交互---》CPU通过地址总线访问内存问题,CPU通过指令地址寄存器取指令,指令地址寄存器的位数16位,能表示的地址为2^16次方B(byte)=64KB,也就是说地址总线只需要16根就行。CPU的寻址能力64KB。64KB太小了,当时程序需要1MB的寻址能力--》增加地址总线到20根--》寄存器16位固定,很难修改--》用2个寄存器表示2^16次方 × 2^4次方=2^20次方=1MB--》一个寄存器对内存进行分段,另一个对段内存进行寻址
栈段寄存器SS
CPL:CS和SS的后两位(RPL)CPL是RPL的特例
控制单元CU
后端
基于底层语言实现,用于管理硬件
RPL
16位
IR(class字节码)
C1Client Compiler
CISC:复杂指令集(Inter)
汇编语言
代码段2描述符
2
编码规则,规定了单词出现的位置
堆内存
地址1
单词流
连续内存
互相转换
语法树
操作系统
C2Server Compiler
主存
OS
指令段直接跳转(方法调用):call指令ret指令为什么要对指令进行分段?不可能将指令一直不停的排下去,指令分段方便管理。指令段相当于(代码段,函数、方法、过程)。指令操作的数据需要放在内存,数据分为私有和共享
段164KB
IR(IR是AST,不是class字节码)
分配内存怎么分配内存?调指令从堆内存中返回一个内存地址
13位
嵌入式,微型处理器可以用ROM保存代码,加速运算,用一点点RAM保存数据。因为数据可读可写,代码是只读的
计算机只认知二进制,地址编号也是二进制
指令流之间共享数据
嵌入式
抽象汇编语言的跳转指令(jmp、jne等)例如:if语句、while语句、for语句等等
相当于定义了接口,冯诺依曼进行了实现
有多种实现方式
内存地址?使用16进制给内存中的每个字节编了一个号,这个号就是内存地址
地址总线AB
指令操作的数据经过移动加减等等操作指令最终都会落地到内存。数据放在内存的哪里?-->需要将内存按照功能分割为几个不同的部分-->指令段中的指令,必定会有自己的数据,有些数据是指令段执行完毕后需要销毁的(局部变量),又有些数据是指令段之间需要共享的(共享变量)-->按照功能将内存存放数据的区域切割即可:指令段私有数据+指令段共享数据-->指令段有多个,代表可以call多个指令-->使用数据结构表示这种存储关系:栈和堆
专用寄存器组
数据总线DB
Linux0.11
第一版操作系统:根据ISA指令集写出来的,用0101写出来的,然后用汇编重写现在操作系统:汇编+C需要控制硬件的用汇编写不需要控制硬件的用C写
BPF VMJIT
Linux
=64KB
基石
AVL
地址2
寄存器1
抽象操作
0000 0000...
保存程序的指令流
机器码
经过语义分析,此时的语法树带有其他信息
为什么需要段寄存器
栈帧:栈的一片空间里存储了一组数据如何开辟?-->引入数据结构的栈-->不管什么数据结构,都得先来一片内存-->内存分为:连续和不连续连续的栈需要用栈顶和栈底指定栈的界限(关注这个)不连续的栈可以用反向链表实现执行指令段A的时候,怎么知道A的栈帧在哪?需要有栈顶寄存器和栈底寄存器指向A的栈帧里的栈顶和栈底指令段A调用指令段B的时候,开辟B的栈帧,需要保留指令段A的栈底寄存器和指令段A的下一条指令地址(指令3的地址),指令段B执行完恢复A的栈底,继续执行指令段A里的指令。
生产class字节码
不可见部分
JIT即时编译器
编译器结构
1个Item=64位=8B8192*8=65536B=64KB
栈底
LLVM
BPF
实模式:操作的是物理地址保护模式:操作的是虚拟地址I286开始需要提供保护功能,为了防止内存踩踏,不能再用以前的这种内存访问方式。I386是真正的32位机,寄存器32位,地址总线32位,段寄存器16位2^32次方=4GB,那么一个寄存器就可以表示所有的地址了,段寄存器是不是显得很多余?--》并不会多余。用来提供保护功能。通过CS段寄存器查表,得到代码段描述符的首地址段描述符的首地址=CS段寄存器的前13位*8+gdtr寄存器的前32位(乘8也就是左移二进制的3位,因为CS寄存器的后三位用来表示特权级和其他信息,gdtr寄存器48位,前32位表示GDT表的首地址,后16位表示表的长度,也就是2^16=64KB)段描述符的首地址可以定位到GDT表的代码\\数据\\栈段描述符。得到描述符里的段基地址指令地址寄存器存虚拟地址虚拟地址+描述符中的段基地址=线性地址虚拟地址就是逻辑地址
不连续内存
一条指令可以完成好几条指令工作压力在cpu,cpu需要提供一个程序用于将一条指令拆解成多条指令
操作系统也是一个程序(只不过有点特殊)-->程序运行在内存-->操作系统也运行在内存-->问题:操作系统在内存,其他程序也在内存,其他程序可以改操作系统的内存吗?-->不能,操作系统的地位比较特殊,它管理了一切,如果让你随便搞,操作系统将变得易碎且不安全
编译器
运算单元AU
动态分配
内存分段
控制总线
给定输入,按照规则和限制输出
使用16进制进行编码,避免二进制位数过多。由于对地址进行了编码,地址有了高低之分:高地址,低地址使用16进制给内存中的每个字节编了一个号,这个号就是内存地址
硬盘
写32个0
CPL
使用电容实现
RubyInterpreter
LLVM IR链接器
地址
GDTR全局段描述符表寄存器
栈段2描述符
三明治架构一切组件均可单独出来使用全部流程均可使用插件机制进行配置选择全部模块化--->高度可定制--->所有语言均可按照模块化机制接入
JavaScript
段寄存器16位
Java的AST(抽象语法树)解释器框架
规定了一组二进制的编码结构(规定好01怎么放)CPU控制单元可以识别这种编码结构的二进制数需要考虑用前几位用来表示操作码,后几位用来表示操作数
写ISA控制CPU从而操作其他一切
VM
分析优化
类型系统
控制一切
C语言
栈顶寄存器SP
指令段B私有数据B的栈帧
A指令3地址
ROM
栈顶
中间表示IR
计算机有了0和1可以表示信息,怎么表示信息?使用编码,进制转换也是编码的一种实现cpu怎么识别0和1?cpu不认识人类的数字,cpu认识的是电压,那就规定一段电压表示低电压,一段电压表示高低压。低电压表示0,高电压表示1。那么一组0101再物理上的表示就是一组高低电压
根据汇编器规则写汇编代码代码:.text 定义代码段数据:.data 定义数据段英文+:给不同指令流片段定义名字(call)-->汇编器会将这些英文+冒号视为指令流片段的起始内存地址
保存程序的数据(编译时就存在的数据,静态数据)保存在汇编代码.data段定义的数据
L
指令y
SRAM
例如:8086CPU,控制总线16位,地址总线20位,数据总线16位控制总线决定的控制的种类就是2的16次方地址总线决定的寻址能力就是2的20次方byte=1Mb数据总线一次可以传输2byte数据
GDT
Intel手册
GDT表的长度2^16=64KB
多个指令流 共享 数据保存程序运行时动态数据
AT&T编码
计算机编程语言的本质:对下一级语言进行高度抽象
4GB
怎么知道当前执行到那条指令?跳转需要跳到那里去?
call _find_empty_process
Ruby AST
G是1,代表4KBSegment Limit(段界限):2^20Base Address(段基址):04GB=4KB ✖ 2^20因为所有的代码和数据的段基址都为0,而指令地址寄存器又是32位==》那么段描述符的段基址+指令地址寄存器=0+指令地址寄存器==》正好能访问所有4GB==》Linux中没有使用分段机制为什么这么设计?因为intel手册规定了必须要经过分段。那行Linux直接将整个内存设置为1个段,段基址为0指令地址寄存器的值是虚拟地址、线性地址、逻辑地址
指令
栈帧
定义、识别单词
位数决定了控制的种类。2^n
编译原理
IR用于解耦
32位
代码段寄存器CS
JVM里搭配使用
数据段1描述符
指向表的首地址
从cpu角度看对内存数据进行加一操作:1.将内存中的数据加载到寄存器(不越过寄存器操作内存,因为速度太慢)2.对寄存器中的数据加一3.将寄存器中的数据写回内存
速度:缓存>内存>硬盘容量:缓存<内存<硬盘速度越快越贵容量越快越小
4位表示段界限
一个寄存器能表示多少个1byte的数据?一般情况下,寄存器的位数跟CPU的位数有关。CPU的位数16位,32位,64位。
专用寄存器:只能用于它所表明的用途
根据程序内存分布得出段寄存器的分类
指令段B
地址总线
研究硬件将cpu、内存、外设、硬盘分解,研究它们在数字逻辑层面上的设计,保证更小的单元、更多的逻辑、更高的性能、更低的功耗
PythonInterpreter
BPF在内核中添加了一个VM,原来BPF的VM支持一些操作数据包的字节码,现在需要做更多的事,那么就增加一些BPF的字节码和可操作空间-->Extend BPF-->EBPF-->BPF
JVM Runtiem Mode:运行在Hotspot上就是默认的JIT编译器Native Image:可以把java代码编译为可执行的二进制文件,生成的可执行文件不依赖于JVM,只于操作系统和CPU架构相关Java on Truffle:通过 Truffle 框架运行 Java 代码
多个指令流 私有 数据栈的特性:后进先出,栈顶的内存就是存储正在执行的代码段操作的数据保存程序运行时的动态数据
Truffle Framework
Uinx
4G=2^32byte
系统调用协议
基本都是操作内存和寄存器的
种子
CPU从缓存读,缓存没有到内存读,内存没有到硬盘读
在上面的栈内存开辟B的栈帧
物理(电学)
运算符
对着ISA指令集写01太多,找bug麻烦,阅读麻烦,所以引入了汇编代码
表示输入的层级结构
1
指令x
进程管理
算法
识别每个单词的词性和意思
汇编取决于ISA指令集,指令集不一样,汇编语言就不一样。汇编语言与指令集相关-->指令集与cpu相关-->汇编代码就是面向cpu编程,操作cpu控制其他硬件完成操作具象:操作cpu的控制单元,控制寄存器单元和运算单元对来自缓存中的数据进行crud抽象了ISA指令集的规则,提供了人们方便查看和编写的代码方式--助记符汇编操作CPU的寄存器与运算单元,然后将结果写入内存
RISC:精简指令集(ARM)
JVMJIT、解释器
3
可以被编译器,链接器、加载器、操作系统创建,绝不能被用户态的应用程序创建
指向当前执行指令的地址,默认一直往下执行,根据状态寄存器进行跳转
系统总线Bus
类比
指明当前指令要完成什么操作
伪三明治架构设计之初就没考虑兼容多语言没有模块化,不具备扩展性,写前端需要了解前端后端整套流程
运算器
通用寄存器组
1这个数据本身就存在指令中,指令流又存在代码段内存中-->数据存在代码段中
网络栈
CPU和内存需要交互CPU控制了内存通常总线与CPU的位数相等
特权级
语言1前端
冯诺依曼体系结构
控制总线CB
寄存器
用于指明需要控制的设备进行读\\写是哪一个地址
存储器
程序1虚拟内存
浏览器
指明当前指令参与操作的东西(寄存器?内存中的值?)
可见部分
2^16byte
int find_empty_process(void){int i;repeat:if ((++last_pid)<0) last_pid=1;for(i=0 ; i<NR_TASKS ; i++)if (task[i] && task[i]->pid == last_pid) goto repeat;for(i=1 ; i<NR_TASKS ; i++)if (!task[i])return i;return -EAGAIN;}
语言2前端
HTTP协议
0 条评论
下一页