Java的模板解析执行需要模板表与转发表的支持,而这2个表中的数据在HotSpot虚拟机启动时就会初始化。这一篇首先介绍模板表。

在启动虚拟机阶段会调用init_globals()方法初始化全局模块,在这个方法中通过调用interpreter_init()方法初始化模板解释器,调用栈如下:

TemplateInterpreter::initialize()    templateInterpreter.cpp
interpreter_init() interpreter.cpp
init_globals() init.cpp
Threads::create_vm() thread.cpp
JNI_CreateJavaVM() jni.cpp
InitializeJVM() java.c
JavaMain() java.c
start_thread() pthread_create.c

interpreter_init()方法主要是通过调用TemplateInterpreter::initialize()方法来完成逻辑,initialize()方法的实现如下:

源代码位置:/src/share/vm/interpreter/templateInterpreter.cpp

void TemplateInterpreter::initialize() {
if (_code != NULL)
return; // 抽象解释器AbstractInterpreter的初始化,AbstractInterpreter是基于汇编模型的解释器的共同基类,
// 定义了解释器和解释器生成器的抽象接口
AbstractInterpreter::initialize(); // 模板表TemplateTable的初始化,模板表TemplateTable保存了各个字节码的模板
TemplateTable::initialize(); // generate interpreter
{
ResourceMark rm;
int code_size = InterpreterCodeSize;
// CodeCache的Stub队列StubQueue的初始化
_code = new StubQueue(new InterpreterCodeletInterface, code_size, NULL,"Interpreter");
// 实例化模板解释器生成器对象TemplateInterpreterGenerator
InterpreterGenerator g(_code);
} // initialize dispatch table
_active_table = _normal_table;
}

模板解释器的初始化包括如下几个方面:

(1)抽象解释器AbstractInterpreter的初始化,AbstractInterpreter是基于汇编模型的解释器的共同基类,定义了解释器和解释器生成器的抽象接口。

(2)模板表TemplateTable的初始化,模板表TemplateTable保存了各个字节码的模板(目标代码生成函数和参数);

(3)CodeCache的Stub队列StubQueue的初始化;

(4)解释器生成器InterpreterGenerator的初始化。

在之前介绍过,在TemplateInterpreter::initialize() 中通过调用语句来间接调用generate_method_entry()和generate_normal_entry()创建方法执行的栈帧:

InterpreterGenerator g(_code);

不过在如上语句调用之前,首先需要调用TemplateInterpreter类中的initialize()方法初始化模板表,如下:

TemplateTable::initialize();

模板表TemplateTable保存了各个字节码的模板(目标代码生成函数和参数),initialize()方法的实现如下:

源代码位置:/src/share/vm/interpreter/templateInterpreter.cpp

void TemplateTable::initialize() {
if (_is_initialized) return; _bs = Universe::heap()->barrier_set(); // For better readability
const char _ = ' ';
const int ____ = 0;
const int ubcp = 1 << Template::uses_bcp_bit;
const int disp = 1 << Template::does_dispatch_bit;
const int clvm = 1 << Template::calls_vm_bit;
const int iswd = 1 << Template::wide_bit;
// interpr. templates
// Java spec bytecodes ubcp|disp|clvm|iswd in out generator argument
def(Bytecodes::_nop , ____|____|____|____, vtos, vtos, nop , _ );
def(Bytecodes::_aconst_null , ____|____|____|____, vtos, atos, aconst_null , _ );
def(Bytecodes::_iconst_m1 , ____|____|____|____, vtos, itos, iconst , -1 );
def(Bytecodes::_iconst_0 , ____|____|____|____, vtos, itos, iconst , 0 );
// ...
def(Bytecodes::_tableswitch , ubcp|disp|____|____, itos, vtos, tableswitch , _ );
def(Bytecodes::_lookupswitch , ubcp|disp|____|____, itos, itos, lookupswitch , _ );
def(Bytecodes::_ireturn , ____|disp|clvm|____, itos, itos, _return , itos );
def(Bytecodes::_lreturn , ____|disp|clvm|____, ltos, ltos, _return , ltos );
def(Bytecodes::_freturn , ____|disp|clvm|____, ftos, ftos, _return , ftos );
def(Bytecodes::_dreturn , ____|disp|clvm|____, dtos, dtos, _return , dtos );
def(Bytecodes::_areturn , ____|disp|clvm|____, atos, atos, _return , atos );
def(Bytecodes::_return , ____|disp|clvm|____, vtos, vtos, _return , vtos );
def(Bytecodes::_getstatic , ubcp|____|clvm|____, vtos, vtos, getstatic , f1_byte );
def(Bytecodes::_putstatic , ubcp|____|clvm|____, vtos, vtos, putstatic , f2_byte );
def(Bytecodes::_getfield , ubcp|____|clvm|____, vtos, vtos, getfield , f1_byte );
def(Bytecodes::_putfield , ubcp|____|clvm|____, vtos, vtos, putfield , f2_byte );
def(Bytecodes::_invokevirtual , ubcp|disp|clvm|____, vtos, vtos, invokevirtual , f2_byte );
def(Bytecodes::_invokespecial , ubcp|disp|clvm|____, vtos, vtos, invokespecial , f1_byte );
def(Bytecodes::_invokestatic , ubcp|disp|clvm|____, vtos, vtos, invokestatic , f1_byte );
def(Bytecodes::_invokeinterface , ubcp|disp|clvm|____, vtos, vtos, invokeinterface , f1_byte );
def(Bytecodes::_invokedynamic , ubcp|disp|clvm|____, vtos, vtos, invokedynamic , f1_byte );
def(Bytecodes::_new , ubcp|____|clvm|____, vtos, atos, _new , _ );
def(Bytecodes::_newarray , ubcp|____|clvm|____, itos, atos, newarray , _ );
def(Bytecodes::_anewarray , ubcp|____|clvm|____, itos, atos, anewarray , _ );
def(Bytecodes::_arraylength , ____|____|____|____, atos, itos, arraylength , _ );
def(Bytecodes::_athrow , ____|disp|____|____, atos, vtos, athrow , _ );
def(Bytecodes::_checkcast , ubcp|____|clvm|____, atos, atos, checkcast , _ );
def(Bytecodes::_instanceof , ubcp|____|clvm|____, atos, itos, instanceof , _ );
def(Bytecodes::_monitorenter , ____|disp|clvm|____, atos, vtos, monitorenter , _ );
def(Bytecodes::_monitorexit , ____|____|clvm|____, atos, vtos, monitorexit , _ );
def(Bytecodes::_wide , ubcp|disp|____|____, vtos, vtos, wide , _ );
def(Bytecodes::_multianewarray , ubcp|____|clvm|____, vtos, atos, multianewarray , _ );
def(Bytecodes::_ifnull , ubcp|____|clvm|____, atos, vtos, if_nullcmp , equal );
def(Bytecodes::_ifnonnull , ubcp|____|clvm|____, atos, vtos, if_nullcmp , not_equal );
def(Bytecodes::_goto_w , ubcp|____|clvm|____, vtos, vtos, goto_w , _ );
def(Bytecodes::_jsr_w , ubcp|____|____|____, vtos, vtos, jsr_w , _ ); // wide Java spec bytecodes
def(Bytecodes::_iload , ubcp|____|____|iswd, vtos, itos, wide_iload , _ );
def(Bytecodes::_lload , ubcp|____|____|iswd, vtos, ltos, wide_lload , _ );
// ... // JVM bytecodes
def(Bytecodes::_fast_agetfield , ubcp|____|____|____, atos, atos, fast_accessfield , atos );
def(Bytecodes::_fast_bgetfield , ubcp|____|____|____, atos, itos, fast_accessfield , itos );
def(Bytecodes::_fast_cgetfield , ubcp|____|____|____, atos, itos, fast_accessfield , itos );
def(Bytecodes::_fast_dgetfield , ubcp|____|____|____, atos, dtos, fast_accessfield , dtos );
def(Bytecodes::_fast_fgetfield , ubcp|____|____|____, atos, ftos, fast_accessfield , ftos );
def(Bytecodes::_fast_igetfield , ubcp|____|____|____, atos, itos, fast_accessfield , itos );
def(Bytecodes::_fast_lgetfield , ubcp|____|____|____, atos, ltos, fast_accessfield , ltos );
def(Bytecodes::_fast_sgetfield , ubcp|____|____|____, atos, itos, fast_accessfield , itos ); def(Bytecodes::_fast_aputfield , ubcp|____|____|____, atos, vtos, fast_storefield , atos );
def(Bytecodes::_fast_bputfield , ubcp|____|____|____, itos, vtos, fast_storefield , itos );
def(Bytecodes::_fast_cputfield , ubcp|____|____|____, itos, vtos, fast_storefield , itos );
def(Bytecodes::_fast_dputfield , ubcp|____|____|____, dtos, vtos, fast_storefield , dtos );
def(Bytecodes::_fast_fputfield , ubcp|____|____|____, ftos, vtos, fast_storefield , ftos );
def(Bytecodes::_fast_iputfield , ubcp|____|____|____, itos, vtos, fast_storefield , itos );
def(Bytecodes::_fast_lputfield , ubcp|____|____|____, ltos, vtos, fast_storefield , ltos );
def(Bytecodes::_fast_sputfield , ubcp|____|____|____, itos, vtos, fast_storefield , itos ); def(Bytecodes::_fast_aload_0 , ____|____|____|____, vtos, atos, aload , 0 );
def(Bytecodes::_fast_iaccess_0 , ubcp|____|____|____, vtos, itos, fast_xaccess , itos );
def(Bytecodes::_fast_aaccess_0 , ubcp|____|____|____, vtos, atos, fast_xaccess , atos );
def(Bytecodes::_fast_faccess_0 , ubcp|____|____|____, vtos, ftos, fast_xaccess , ftos ); def(Bytecodes::_fast_iload , ubcp|____|____|____, vtos, itos, fast_iload , _ );
def(Bytecodes::_fast_iload2 , ubcp|____|____|____, vtos, itos, fast_iload2 , _ );
def(Bytecodes::_fast_icaload , ubcp|____|____|____, vtos, itos, fast_icaload , _ ); def(Bytecodes::_fast_invokevfinal , ubcp|disp|clvm|____, vtos, vtos, fast_invokevfinal , f2_byte ); def(Bytecodes::_fast_linearswitch , ubcp|disp|____|____, itos, vtos, fast_linearswitch , _ );
def(Bytecodes::_fast_binaryswitch , ubcp|disp|____|____, itos, vtos, fast_binaryswitch , _ ); def(Bytecodes::_fast_aldc , ubcp|____|clvm|____, vtos, atos, fast_aldc , false );
def(Bytecodes::_fast_aldc_w , ubcp|____|clvm|____, vtos, atos, fast_aldc , true ); def(Bytecodes::_return_register_finalizer , ____|disp|clvm|____, vtos, vtos, _return , vtos ); def(Bytecodes::_invokehandle , ubcp|disp|clvm|____, vtos, vtos, invokehandle , f1_byte ); def(Bytecodes::_shouldnotreachhere , ____|____|____|____, vtos, vtos, shouldnotreachhere , _ );
// platform specific bytecodes
pd_initialize(); _is_initialized = true;
}

TemplateTable的初始化调用def()将所有字节码的目标代码生成函数和参数保存在_template_table或_template_table_wide(wide指令)模板数组中。除了虚拟机规范本身定义的字节码指令外,HotSpot虚拟机也定义了一些字节码指令,这些指令为了辅助虚拟机进行更好、理简单的功能实现,例如Bytecodes::_return_register_finalizer等在之前已经介绍过,可以更好的实现finalizer类型对象的注册功能。

对于调用def()函数时传递的一些参数在后面会解释。def()函数有2个,接收的参数不同,实现如下:

void TemplateTable::def(
Bytecodes::Code code, // 字节码指令
int flags, // 标志位
TosState in, // 模板执行前TosState
TosState out, // 模板执行后TosState
void (*gen)(), // 模板生成器,是模板的核心组件
char filler
) {
assert(filler == ' ', "just checkin'");
def(code, flags, in, out, (Template::generator)gen, 0); // 调用下面的def()函数
} void TemplateTable::def(
Bytecodes::Code code, // 字节码指令
int flags, // 标志位
TosState in, // 模板执行前TosState
TosState out, // 模板执行后TosState
void (*gen)(int arg), // 模板生成器,是模板的核心组件
int arg
) {
// should factor out these constants
const int ubcp = 1 << Template::uses_bcp_bit; // 表示是否需要bcp指针
const int disp = 1 << Template::does_dispatch_bit; // 表示是否在模板范围内进行转发
const int clvm = 1 << Template::calls_vm_bit; // 表示是否需要调用JVM函数
const int iswd = 1 << Template::wide_bit; // 表示是否为wild指令 // determine which table to use
bool is_wide = (flags & iswd) != 0; // make sure that wide instructions have a vtos entry point
// (since they are executed extremely rarely, it doesn't pay out to have an
// extra set of 5 dispatch tables for the wide instructions - for simplicity
// they all go with one table)
assert(in == vtos || !is_wide, "wide instructions have vtos entry point only");
Template* t = is_wide ? template_for_wide(code) : template_for(code); // setup entry
t->initialize(flags, in, out, gen, arg); // 调用模板表t的initialize()方法初始化模板表
assert(t->bytecode() == code, "just checkin'");
}

模板表由模板表数组与一组生成器组成:

(1)模板表数组有_template_table与_template_table_wild,数组的下标为bytecode,值为Template,按照字节码指令的操作码递增顺序排列。

(2)一组生成器,所有与bytecode配套的生成器,在初始化模板表时作为gen参数传给相应的Template。

Template类的定义如下:

源代码位置:hotspot/src/share/vm/interpreter/templateTable.hpp
// A Template describes the properties of a code template for a given bytecode
// and provides a generator to generate the code template. class Template VALUE_OBJ_CLASS_SPEC {
private:
enum Flags {
// 字节码指令指的是该字节码的操作数是否存在于字节码里面
uses_bcp_bit, // set if template needs the bcp pointing to bytecode
does_dispatch_bit,// set if template dispatches on its own
calls_vm_bit, // set if template calls the vm
wide_bit // set if template belongs to a wide instruction
}; typedef void (*generator)(int arg); int _flags; // describes interpreter template properties (bcp unknown)
TosState _tos_in; // tos cache state before template execution
TosState _tos_out; // tos cache state after template execution
generator _gen; // template code generator
int _arg; // argument for template code generator
... // Templates
static Template* template_for(Bytecodes::Code code) {
Bytecodes::check(code);
return &_template_table[code];
}
static Template* template_for_wide(Bytecodes::Code code) {
Bytecodes::wide_check(code);
return &_template_table_wide[code];
}
};

调用的template_for()与template_for_wild()方法从_template_table或_template_for_wild数组中取值。这2个变量定义在TemplateTable类中,如下:

static Template        _template_table     [Bytecodes::number_of_codes];
static Template _template_table_wide[Bytecodes::number_of_codes];

继续看TemplateTable::def(0函数的各个参数,解释如下:

(1)_flags:是一个标志,低四位分别表示:

  • uses_bcp_bit,标志需要使用字节码指针(byte code pointer,数值为字节码基址+字节码偏移量)
  • does_dispatch_bit,标志是否在模板范围内进行转发,如跳转类指令会设置该位
  • calls_vm_bit,标志是否需要调用JVM函数
  • wide_bit,标志是否是wide指令(使用附加字节扩展全局变量索引)

(2)_tos_in:表示模板执行前的TosState(操作数栈栈顶元素的数据类型,TopOfStack,用来检查模板所声明的输出输入类型是否和该函数一致,以确保栈顶元素被正确使用)

(3)_tos_out:表示模板执行后的TosState

(4)_gen:表示模板生成器(函数指针)

(5)_arg:表示模板生成器参数

再来看一下TemplateTable::initialize()方法中对def()函数的调用,以_iinc(将局部变量增加1)为例,调用如下:

def(
Bytecodes::_iinc, // 字节码指令
ubcp|____|clvm|____, // 标志
vtos, // 模板执行前的TosState
vtos, // 模板执行后的TosState
iinc , // 模板生成器,是一个iinc()函数的指针
_ // 不需要模板生成器参数
); 

设置标志位uses_bcp_bit和calls_vm_bit,表示iinc指令的生成器需要使用bcp指针函数at_bcp(),且需要调用JVM函数,下面给出了生成器的定义:

源代码位置:/hotspot/src/cpu/x86/vm/templateTable_x86_64.cpp

void TemplateTable::iinc() {
transition(vtos, vtos);
__ load_signed_byte(rdx, at_bcp(2)); // get constant
locals_index(rbx);
__ addl(iaddress(rbx), rdx);
}

iinc指令的格式如下:

iinc
index
const

操作码iinc占用一个字节,而index与const分别占用一个字节。使用at_bcp()函数获取iinc指令的操作数,2表示偏移2字节,所以会将const取出来存储到rdx中。调用locals_index()函数取出index,locals_index()就是JVM函数。最终生成的汇编如下:

// %r13存储的是指向字节码的指针,偏移2字节后取出const存储到%edx
0x00007fffe101a210: movsbl 0x2(%r13),%edx
// 取出index存储到%ebx
0x00007fffe101a215: movzbl 0x1(%r13),%ebx
0x00007fffe101a21a: neg %rbx
// %r14指向本地变量表的首地址,将%edx加到%r14+%rbx*8指向的内存所存储的值上
// 之所以要对%rbx执行neg进行符号反转,是因为在Linux内核的操作系统上,栈是向低地址方向生长的
0x00007fffe101a21d: add %edx,(%r14,%rbx,8)

不过这里并不会调用iinc()函数生成对应的汇编代码,只是将传递给def()函数的各种信息保存到Template对象中,在TemplateTable::def()方法中,通过template_for()或template_for_wild()方法获取到数组中对应的Template对象后,就会调用Template::initialize()方法,实现如下:

void Template::initialize(int flags, TosState tos_in, TosState tos_out, generator gen, int arg) {
_flags = flags;
_tos_in = tos_in;
_tos_out = tos_out;
_gen = gen;
_arg = arg;
}

可以看到,只是将信息保存到对应的Template对象中,这样就可以根据字节码索引从数组中获取对应的Template对象,进而获取相关信息。下一篇我们将会看到对这些信息的使用。  

相关文章的链接如下:

1、在Ubuntu 16.04上编译OpenJDK8的源代码

2、调试HotSpot源代码

3、HotSpot项目结构 

4、HotSpot的启动过程

5、HotSpot二分模型(1)

6、HotSpot的类模型(2)

7、HotSpot的类模型(3)

8、HotSpot的类模型(4)

9、HotSpot的对象模型(5)

10、HotSpot的对象模型(6)

11、操作句柄Handle(7)

12、句柄Handle的释放(8)

13、类加载器

14、类的双亲委派机制

15、核心类的预装载

16、Java主类的装载

17、触发类的装载

18、类文件介绍

19、文件流

20、解析Class文件

21、常量池解析(1)

22、常量池解析(2)

23、字段解析(1)

24、字段解析之伪共享(2)

25、字段解析(3)

26、字段解析之OopMapBlock(4)

27、方法解析之Method与ConstMethod介绍

28、方法解析

29、klassVtable与klassItable类的介绍

30、计算vtable的大小

31、计算itable的大小

32、解析Class文件之创建InstanceKlass对象

33、字段解析之字段注入

34、类的连接

35、类的连接之验证

36、类的连接之重写(1)

37、类的连接之重写(2)

38、方法的连接

39、初始化vtable

40、初始化itable

41、类的初始化

42、对象的创建

43、Java引用类型

44、Java引用类型之软引用(1)

45、Java引用类型之软引用(2)

46、Java引用类型之弱引用与幻像引用

47、Java引用类型之最终引用

48、HotSpot的垃圾回收算法

49、HotSpot的垃圾回收器

50、CallStub栈帧

51、entry point栈帧

52、generate_fixed_frame()方法生成Java方法栈帧

53、dispatch_next()方法的实现

54、虚拟机执行模式

作者持续维护的个人博客  classloading.com

关注公众号,有HotSpot源码剖析系列文章!

  

JVM的方法执行引擎-模板表的更多相关文章

  1. JVM的方法执行引擎-entry point栈帧

    接着上一篇去讲,回到JavaCalls::call_helper()中: address entry_point = method->from_interpreted_entry(); entr ...

  2. JVM总结(五):JVM字节码执行引擎

    JVM字节码执行引擎 运行时栈帧结构 局部变量表 操作数栈 动态连接 方法返回地址 附加信息 方法调用 解析 分派 –“重载”和“重写”的实现 静态分派 动态分派 单分派和多分派 JVM动态分派的实现 ...

  3. 一夜搞懂 | JVM 字节码执行引擎

    前言 本文已经收录到我的 Github 个人博客,欢迎大佬们光临寒舍: 我的 GIthub 博客 学习导图 一.为什么要学习字节码执行引擎? 代码编译的结果从本地机器码转变为字节码,是存储格式发展的一 ...

  4. 深入理解JVM—字节码执行引擎

    原文地址:http://yhjhappy234.blog.163.com/blog/static/3163283220122204355694/ 前面我们不止一次的提到,Java是一种跨平台的语言,为 ...

  5. JVM字节码执行引擎和动态绑定原理

    1.执行引擎 所有Java虚拟机的执行引擎都是一致的: 输入的是字节码文件,处理过程就是解析过程,最后输出执行结果. 在整个过程不同的数据在不同的结构中进行处理. 2.栈帧 jvm进行方法调用和方法执 ...

  6. 【JVM】JVM系列之执行引擎(五)

    一.前言 在了解了类加载的相关信息后,有必要进行更深入的学习,了解执行引擎的细节,如字节码是如何被虚拟机执行从而完成指定功能的呢.下面,我们将进行深入的分析. 二.栈帧 我们知道,在虚拟机中与执行方法 ...

  7. 图解JVM字节码执行引擎之栈帧结构

    一.执行引擎      “虚拟机”的概念是相对于“物理机”而言的,这两种“机器”都有执行代码的能力.物理机的执行引擎是直接建立在硬件处理器.物理寄存器.指令集和操作系统层面的:而“虚拟机”的执行引擎是 ...

  8. JVM字节码执行引擎

    一.概述 在不同的虚拟机实现里面,执行引擎在执行Java代码的时候可能会有解释执行(通过解释器执行)和编译器执行(通过即时编译器产生本地代码执行)两种选择,所有的Java虚拟机的执行引擎都是一致的:输 ...

  9. JVM 专题十五:执行引擎

    1. 执行引擎概述 1.1 执行引擎 1.2 概述 执行引擎是Java虚拟机的核心组成部分之一. 虚拟机是一个相对于“物理机”的概念,这两种机器都有代码执行能力,其区别是物理机的执行引擎是直接建立在处 ...

随机推荐

  1. 推特(Twitter)如何绑定谷歌二次验证码/谷歌身份验证/双重认证?

    1.下载Twitter,找到双重验证界面 手机连接VPN下载Twitter(获取免费VPN可加微信客服“Ecyzm-”),注册登陆后,点左上角账户头像-Settings and privacy - A ...

  2. webpack 编译时,提示 Unexpected token: keyword «const»

    代码里如果用到const 关键字,编译报这种错误 解决方法: npm install terser-webpack-plugin --save 然后,webpack配置: const TerserPl ...

  3. 2020 年百度之星&#183;程序设计大赛 - 初赛三

    2020 年百度之星·程序设计大赛 - 初赛三解题思路及代码(Discount.Game.Permutation) 1.Discount Problem Description学皇来到了一个餐馆吃饭. ...

  4. Ansible部署zabbix-agent

    playbook目录 zabbix/ ├── hosts ##定义的主机列表 ├── install_zabbix_agent.yml ##安装入口文件 └── roles ├── install_z ...

  5. scrapy基本用法

    scrapy官方文档http://doc.scrapy.org/en/latest/ 一.scrapy安装 安装lxml:pip3 install lxml 安装wheel:pip3 install ...

  6. paramiko上传文件到Linux

    一.传输单个文件到Linux服务器 import paramiko transport = paramiko.Transport(('host',22)) transport.connect(user ...

  7. XML--概念、约束、解析

    概念 XML:可扩展标记语言 HTML:超文本标记语言 两者的区别: 1.语法结构类似,单语法要求不同 HTML不区分大小写,XML严格区分大小写 在HTML中,有时不严格,如果上下文清楚地显示出段落 ...

  8. js原声代码 轮播图

    js轮播图 html部分:建立div,内嵌img标签,可以设置大小, <!doctype html> <html> <head> <meta charset= ...

  9. Python打印到屏幕_读取键盘输入

    Python打印到屏幕_读取键盘输入: print( ): 打印输出括号中的值 print("hello") # hello strs = 'hello' print(" ...

  10. 小甲鱼零基础汇编语言学习笔记第二章之寄存器(CPU工作原理,CPU内部通讯)

    这一章主要介绍了CPU中的重要器件——寄存器,整个系列通篇是以8086CPU作为探讨对象,其它更高级的CPU都是在此基础之上进行的升级.   1.一个典型的CPU是由运算器.控制器.寄存器等器件组成, ...