「MoreThanJava」机器指令到汇编再到高级编程语言

「MoreThanJava」机器指令到汇编再到高级编程语言

  • 「MoreThanJava」 张扬的是 「学习,不止 CODE」,本系列 Java 基础教程是自己在连系各方面的知识之后,对 Java 基础的一个总回首,旨在 「辅助新同伙快速高质量的学习」
  • 固然 岂论新老同伙 我信赖您都可以 从中获益。若是以为 「不错」 的同伙,迎接 「关注 + 留言 + 分享」,文末有完整的获取链接,您的支持是我前进的最大的动力!

Part 1. 机械指令

「MoreThanJava」机器指令到汇编再到高级编程语言

上一次 我们已经领会了 二进制和 CPU 的基本原理,知道了程序运行时,CPU 每秒数以亿次、十亿次、百亿次地震荡着时钟,同步执行着细小的 「电子操作」,例如:从内存读取一个字节的数据到 CPU 又或者判断字节中的某一位是 0 照样 1

CPU 自己有一组 划定好的 可以执行的 「基本动作」(被称为 机械指令):

  1. 读取指令;2. 执行指令;3. 写寄存器;

这险些就是 CPU 事情的所有了。 这些动作虽然每次只能执行一次,然则每秒可以执行数十亿次,这个数量级的「小操作」累加成为一个大的「有用的操作」。

处置器所做的一切都是基于这些细小的操作!幸运的是,我们已经不再需要领会这些操作的详细信息就可以编写和使用各种程序。诸如 Java 这一类的 「高级语言」目的 就是 将这些细小的电子操作组织成由人类可读的「程序语言」示意的大型有用单元

机械指令演示

一条 机械指令 一样平时由内存中的几个字节组成,它们告诉 CPU 应该执行一个什么样的 「机械操作」(是取数据照样写寄存器等..)。处置器依次查看 CPU 中的机械指令,并执行每一条。内存中的一组机械指令被称为 「机械语言程序」,或称为 「可执行程序」

下面我们来使用机械语言来演示一个控制灯泡亮度的机械语言程序。

先和硬件做好划定

假设灯泡由内存中的某一个程序控制,该程序能够完全打开和关闭灯泡,可以使灯泡变亮或变暗,机械指令一个字节长度,而且与机械操作对应如下:

机械指令 机械操作
00000000 住手程序
00000001 完全打开灯泡
00000010 完全关闭灯泡
00000100 灯泡昏暗 10%
00001000 将灯泡照亮 10%
00010000 若是灯泡完全点亮,则跳过下一条说明
00100000 若是灯泡完全熄灭,请跳过下一条说明
01000000 转到程序的最先(地址 0)

Demo 程序 && 演示

凭据上方作出的划定,我们写下如下的程序:(为了利便明白,我把对应的机械操作也写在了后面,现实的程序只包罗机械指令)

地址 机械指令 机械操作
0 00000001 完全打开灯泡
1 00000010 完全关闭灯泡
2 00000001 完全打开灯泡
3 00000100 灯泡昏暗10%
4 00000100 灯泡昏暗10%
5 00000000 住手程序

以是这样的一段程序执行效果就如下图:

「MoreThanJava」机器指令到汇编再到高级编程语言

您可以实验自己行使 01000000(跳转到程序最先) 来改写程序来到达让「灯逐渐变亮又逐渐变暗」的目的。

小结

上面演示的程序 焦点头脑 是:

  • 机械语言程序是内存中一系列机械指令的聚集;
  • 机械指令由一个或多个字节组成(在此示例中,仅一个字节);
  • 处置器一次运行一条机械指令的程序;
  • 所有的小机械操作加起来都是有用的;

在现实的 CPU 中,拥有更多的机械指令,而且更详细,而且差异的 CPU,指令集是差异的。典型的 CPU 拥有一千或更多的机械指令。

Part 2. 汇编语言

「MoreThanJava」机器指令到汇编再到高级编程语言

机械语言太 “反人类”

我们已经可以最先写一些程序使用了,然则使用 机械语言编写代码会十分辛劳,好比:

00000001 00000010 00000001
00000100 00000100 00000000

纵然你刚看过你也会对这一段就在 上方的实例代码 没有什么感知,这是由于机械语言是设计给机械的,人类影象和使用起来就会显得十分贫苦。

云云你就会感知到 上个世纪 的程序员使用 打孔卡片

「MoreThanJava」机器指令到汇编再到高级编程语言

使用 纸带

「MoreThanJava」机器指令到汇编再到高级编程语言

甚至是 直接插拔线路 or 按下开关

「MoreThanJava」机器指令到汇编再到高级编程语言

是一件何等硬核的事情…

「MoreThanJava」机器指令到汇编再到高级编程语言

若是你对它们若何事情以及何等硬核感兴趣,可以参考一下下方的链接:

再附带一个宝藏网站(哥伦比亚大学出书的盘算机历史,异常详细),有条件的同砚 异常推荐 进去浏览一下:

汇编语言降生

CPU 的指令都是 二进制 的,这显然对于人类来说是 不可读 的。为领会决二进制指令的可读性问题,工程师将那些指令写成了 八进制。二进制转八进制是易如反掌的,然则八进制的可读性也不行。

很自然地,最后照样用文字表达,加法指令写成 ADD。内存地址也不再直接引用,而是 用标签 示意。

这样的话,就多出一个步骤,要把这些文字指令翻译成二进制,这个步骤就称为 assembling,完成这个步骤的程序就叫做 assembler。它处置的文本,自然就叫做 aseembly code。标准化以后,称为 assembly language,缩写为 asm,中文译为 汇编语言

「MoreThanJava」机器指令到汇编再到高级编程语言

明白汇编语言

每一种 CPU 的机械指令都是不一样的,因此对应的汇编语言也不一样。本文先容的是现在最常见的 x86 汇编语言,即 Intel 公司的 CPU 使用的那一种。

寄存器

要学习汇编语言,首先必须领会两个知识点:寄存器内存模子

先来看寄存器。CPU 自己只卖力运算,不卖力储存数据。数据一样平时都储存在内存之中,CPU 要用的时刻就去内存读写数据。然则,CPU 的运算速率远高于内存的读写速率,为了制止被拖慢,CPU 都自带一级缓存和二级缓存。基本上,CPU 缓存可以看作是读写速率较快的内存。

然则,CPU 缓存照样不够快,另外数据在缓存内里的地址是不牢固的,CPU 每次读写都要寻址也会拖慢速率。因此,除了缓存之外,CPU 还自带了寄存器(register),用来储存最常用的数据。也就是说,那些最频仍读写的数据(好比循环变量),都市放在寄存器内里,CPU 优先读写寄存器,再由寄存器跟内存交流数据。

「MoreThanJava」机器指令到汇编再到高级编程语言

寄存器不依赖地址区分数据,而依赖名称。每一个寄存器都有自己的名称,我们告诉 CPU 去详细的哪一个寄存器拿数据,这样的速率是最快的。有人比喻寄存器是 CPU 的零级缓存。

寄存器的种类

早期的 x86 CPU 只有 8 个寄存器,而且每个都有差异的用途。现在的寄存器已经有 100 多个了,都酿成通用寄存器,不稀奇指定用途了,然则早期寄存器的名字都被保留了下来。

  • EAX
  • EBX
  • ECX
  • EDX
  • EDI
  • ESI
  • EBP
  • ESP

上面这 8 个寄存器之中,前面七个都是通用的。ESP 寄存器有特定用途,保留当前 Stack 的地址(详见下一节)。

「MoreThanJava」机器指令到汇编再到高级编程语言

我们经常看到 32 位 CPU、64 位 CPU 这样的名称,实在指的就是寄存器的巨细。32 位 CPU 的寄存器巨细就是 4 个字节。

内存模子:Heap(堆)

寄存器只能存放很少量的数据,大多数时刻,CPU 要指挥寄存器,直接跟内存交流数据。以是,除了寄存器,还必须领会内存怎么储存数据。

程序运行的时刻,操作系统会给它分配一段内存,用来储存程序和运行发生的数据。这段内存有起始地址和竣事地址,好比从 0x10000x8000,起始地址是较小的谁人地址,竣事地址是较大的谁人地址。

「MoreThanJava」机器指令到汇编再到高级编程语言

程序运行历程中,对于动态的内存占用请求(好比新建工具,或者使用 malloc 下令),系统就会从预先分配好的那段内存之中,划出一部分给用户,详细规则是从起始地址最先划分(现实上,起始地址会有一段静态数据,这里忽略)。举例来说,用户要求获得 10 个字节内存,那么从起始地址 0x1000 最先给他分配,一直分配到地址 0x100A,若是再要求获得 22 个字节,那么就分配到 0x1020

「MoreThanJava」机器指令到汇编再到高级编程语言

这种由于用户自动请求而划分出来的内存区域,叫做 Heap(堆)。它由起始地址最先,从低位(地址)向高位(地址)增进。Heap 的一个主要特点就是不会自动消逝,必须手动释放,或者由垃圾接纳机制来接纳。

内存模子:Stack(栈)

除了 Heap 以外,其他的内存占用叫做 Stack(栈)。简朴说,Stack 是由于 函数运行暂且占用 的内存区域。

「MoreThanJava」机器指令到汇编再到高级编程语言

例如我们在执行一个叫 main 的函数时,会为它在内存内里建立一个 ,用来保留所有 main 中使用的内部变量。main 函数执行竣事后,该帧就会被接纳,释放所有的内部变量,不再占用空间。

「MoreThanJava」机器指令到汇编再到高级编程语言

若是在 main 函数 内部挪用了其他函数,例如 add_a_and_b 函数,那么执行到这一行的时刻,系统也会为 add_a_and_b 新建一个帧,用来储存它的内部变量。也就是说,此时同时存在两个帧:mainadd_a_and_b。一样平时来说,挪用栈有若干层,就有若干帧。

「MoreThanJava」机器指令到汇编再到高级编程语言

等到 add_a_and_b 运行竣事,它的帧就会被接纳,系统会回到函数 main 适才中止执行的地方,继续往下执行。通过这种机制,就实现了函数的 层层挪用,而且 每一层都能使用自己的内陆变量

我们可以把栈明白为一个下方密封,而上方打开的「桶」。

「MoreThanJava」机器指令到汇编再到高级编程语言

天生的新帧放入我们称之为 「入栈」,而释放帧我们称之为 「出栈」栈的特点 就是,最晚入栈的帧最早出栈(由于最内层的函数挪用,最先竣事运行),这就叫做 “后进先出” 的数据结构。每一次函数执行竣事,就自动释放一个帧,所有函数执行竣事,整个栈就都释放了。

汇编语言演示

举个简朴的例子,我们需要盘算:

(1 + 4) * 2 + 3

我们根据 「后缀示意法」 举行一下转换:

1,4,+,2,*,3,+

我们平时使用的方式是 「中缀示意法」,也就是把盘算符号放中心,例如 1 + 3,后缀则是把符号放最后,例如 1, 3, +

这样做的利益是没有先乘除后加减的影响,也没有括号,直接运算就行了。(例如 1, 3, +,先把 13 保留起来碰着 + 知道是加规则直接相加)

OK,我们重新最先使用汇编语言来编写一下程序,首先第一步:把 1 保留起来(放入寄存器):

Java 异常(二) 自定义异常

MOV  1

之后是 4, +,那就直接加一下:

ADD 4

然后是 2, *,那就直接乘一下(SHL 是向左移动一位的意思,二进制中左移一个单元就相当于乘以 2,例如 01 示意 1,而 10 则示意 2):

SHL 0

最后是 3, +,再加一下:

ADD 3

完整程序如下:

MOV  1
ADD  4
SHL  0
ADD  3

这似乎看起来比 00001111 这样的二进制要好上太多了!程序员们感动到落泪:

「MoreThanJava」机器指令到汇编再到高级编程语言

Part 3. 高级编程语言

「MoreThanJava」机器指令到汇编再到高级编程语言

摆脱了 二进制,我们有了更可读的 汇编语言,但仍然十分繁琐和庞大,每一条汇编指令代表一个基本操作,例如:「从内存 x 位置获取一个数字并放入寄存器 A」、「将寄存器 A 中的数字添加到寄存器 B 的数字上」。这样的编程气概既费时又容易失足,而且一旦失足还很难发现。

例如,我们来看一看 「1969 年阿波罗 11号登月设计」 用来 防止登月舱盘算机耗尽自身资源 的 BAILOUT 代码:

POODOO    INHINT
    CA  Q
    TS  ALMCADR
 
    TC  BANKCALL
    CADR  VAC5STOR  # STORE ERASABLES FOR DEBUGGING PURPOSES.
 
    INDEX  ALMCADR
    CAF  0
ABORT2    TC  BORTENT
 
OCT77770  OCT  77770    # DONT MOVE
    CA  V37FLBIT  # IS AVERAGE G ON
    MASK  FLAGWRD7
    CCS  A
    TC  WHIMPER -1  # YES.  DONT DO POODOO.  DO BAILOUT.
 
    TC  DOWNFLAG
    ADRES  STATEFLG
 
    TC  DOWNFLAG
    ADRES  REINTFLG
 
    TC  DOWNFLAG
    ADRES  NODOFLAG
 
    TC  BANKCALL
    CADR  MR.KLEAN
    TC  WHIMPER

似乎不太容易读的样子…

阿波罗登月设计的源代码在 Github 上已经公然,有兴趣的可以去下方链接膜拜一下(可以去感受一下那时程序员的工程能力):

另外附一下那时代码的设计卖力人 Margaret Heafield Hamilton(女程序员)和完成的堆起来跟人一样高的代码量:
「MoreThanJava」机器指令到汇编再到高级编程语言

第一个高级语言:FORTRAN

John Backus1950 年以一名科学程序员的身份加入 IBM 时,已经可以使用诸如 ADD 之类的助记词取代数字代码来编写程序,也就是我们的汇编语言。这使编程变得容易一些,然则纵然是一个简朴的程序也需要数十次操作,而且仍然很难找到错误。

巴克斯以为,应该有可能建立一种编程语言,使一系列盘算可以用类似于数学符号的形式来表达。然后,使用特定的翻译程序(以今天的术语来说是编译器)可以将其转换为盘算机可以明白的数字代码。

Backus 在 1953 年向他的司理提出了这个想法。他获得了预算,并被激励招聘一个小团队来测试该想法的可行性。三年后,该团队公布了一本手册,其中形貌了 IBM Mathematical Formula Translating System(简称 FORTRAN)。不久之后, IBM 向 IBM 704 的用户提供了第一个 FORTRAN 编译器。

「MoreThanJava」机器指令到汇编再到高级编程语言

Backus 和他的团队缔造了天下上第一种高级编程语言。科学家和工程师将不再需要将其程序编写为数字代码或冗长的助记符

FORTRAN 代码演示

下面演示盘算并输出 8 * 6 的代码实例:

program VF0944
implicit none

integer a, b, c
a= 8
b= 6
c= a*b

print *, 'Hello World, a, b, c= ', a, b, c
end program VF0944

对比汇编代码,是不是看上去要清晰(人类可读)多了呢?

FORTRAN 的意义

FORTRAN 的问世在盘算机史上具有划时代的意义,它使盘算机语言从原始的低级汇编语言走出来,进入了更高的境界,使得 盘算机语言不再是盘算机专家的专利,使宽大的工程技术职员有了举行盘算机编程的手段。

由此盘算机更快地深入到了社会之中,它在工业部门中初露头角,更是在火箭、导弹、人造地球卫星的设计中大显身手,因此有人称 FORTRAN 语言使盘算机的工业应用成了可能,是推动第二次天下大战以后西方工业经济苏醒和进入第二次工业革命的无形气力,是 “看不见的蒸汽机”。

FORTRAN 后时代

FORTRAN 高级程序设计语言的泛起孕育了盘算机软件业,继其之后,盘算机高级程序语言的开发进入到了一个蓬勃发展的时代。

1959

Grace Hopper 发现晰第一个面向企业营业的编程语言,又称 “面向商业的通用语言”,也经常简称 COBOL。

1964

美国达特茅斯学院约翰·凯梅尼和托马斯·卡茨以为,像 FORTRAN 那样的语言太过专业,编程异常难题。于是他们简化了 FORTRAN,并设计出了更适合初学者的 BASIC 语言。

1970

尼古拉斯·沃斯异常痴迷于编程语言,他率先提出了结构化程序设计头脑并发现晰 Pascal 语言。

此外他还提出了 Wirth 定律,意为 “软件变慢的速率比硬件变快的速率更快”,这让摩尔定律变得充满取笑。之后的 Electron.js 也确实证明晰这一点。

1972

丹尼斯·里奇在贝尔实验室事情时代发现晰 C 语言,开启了现代程序语言的革命。之后,他又添加了段错误和其他一些辅助开发职员的实用功效,大大提升了编程效率。

除了 C 语言之外, 他和贝尔实验室的同事还缔造了伟大的 Unix 操作系统。

1980

Alan Kay 发现晰面向工具的编程语言 Smalltalk,在 Smalltalk 中,一切皆工具。

1987

拉里·沃尔发现晰 Perl 语言。

1983

Jean Ichbiah 发现 Ada Lovelace 的程序从未运行成功过,因此决议用她的名字建立一种语言,于是 Ada 语言降生了。

1986

Brac Box 和 Tol Move 通过融合 C 语言和 Smalltalk 的特征,发现晰 Objective-C。但由于其语法艰涩,不太容易明白。

1983

Bjarne Stroustrup 在 C 语言的基础上引入并扩充了面向工具的观点,发现晰—种新的程序语言并将其命名为 C++。

C++ 大大提升了应用程序的编程效率。

1991

Guido van Rossum 憎恶带有大括号的编程语言,于是他参考 Monty Python 和 Flying Circus 语法,并发现晰 Python。

1993

Roberto Ierusalimschy 和其同伙缔造了一门巴西内陆的剧本语言。在内陆化历程中,由于一个小的错误使得索引从1最先,而不是0。这门语言就是 Lua。

1994

Rasmus Lerdorf 为他小我私家主页的 CGI 剧本制作了一个模板引擎,用来统计他自己网站的接见量。

这个文件被上传到网上之后用它的人越来越多。厥后又用 C 语言重新编写,还添加了数据库接见功效。这门语言就是 PHP。

1995

松本行弘发现晰 Ruby 语言。

1995

Brendan Eich 行使周末时间设计了一种语言,用于为天下各地的网页浏览器提供支持,并最终推出了 Skynet。他最初去了 Netscape,并将这门语言命名为 LiveScript,厥后在代码审查时代 Java 逐渐最先风靡,因此他们决议将其改名为 JavaScript。

厥后 Java 使其陷入了商标贫苦,于是 JavaScript 被更名为 ECMAScript。然则人们照样习惯称之为 JavaScript。

1996

James Gosling 发现晰 Java,这是 第一个真正意义上面向工具得编程语言,其中设计模式在实用主义中占统治职位。

More…

对于这一段盘算机历史感兴趣的同砚可以拜读一下「IT 通史 12.2 节 – 高级盘算机程序设计语言」的内容,在线预览链接如下:

高级语言分类

CPU 终究只熟悉二进制指令,在我们发现高级语言之后,仍然无可制止的需要举行 「翻译」 事情。根据翻译方式的差异,我们又把高级语言分为了 「编译型」「注释型」

编译型

「MoreThanJava」机器指令到汇编再到高级编程语言

编译型专业注释为:

使用 专门的编译器,针对 特定的平台,将高级语言源代码 一次性 的编译成可被该平台硬件执行的机械码,并包装成该平台所能识别的可执行性程序的花样,而且只需要编译一次,以后再也不用编译。实在可以简朴明白成谷歌/ 百度翻译,我们把要翻译的文字所有放进去,一次翻译,下次使用直接使用上一次翻译好的效果。

「MoreThanJava」机器指令到汇编再到高级编程语言

  • 优点(较注释型):执行效率高(有注释器省去许多翻译的历程)
  • 瑕玷(较注释型):开发效率低(写完所有的代码才气检查 bug,得多恐怖呀???)

注释型

「MoreThanJava」机器指令到汇编再到高级编程语言

注释型专业注释为:

使用 专门的注释器 对源程序逐行注释成 特定平台 的机械码并 立刻执行,它不需要事先编译,直接将代码注释称机械码直接运行,也就是说只要某一平台提供了响应的注释器即可运行代码。实在可以明白成同声传译,我们需要翻译的时刻,找一个通译员,对方说一句通译员翻译一句,下次翻译照样需要一个通译员一句一句的翻译。

「MoreThanJava」机器指令到汇编再到高级编程语言

  • 瑕玷(较编译型):执行效率低(写一次翻译一次)
  • 优点(较编译型):开发效率高(写一行翻译一行,错了马上就知道,妈妈再也不用忧郁我找不到 bug 了)

半注释半编译的 Java

差异厂商、差异时间开发的 CPU 的指令集是不一样的,这就是上方为什么提到要使用 专门的注释器,要用于 特定的平台 的缘故原由。

以是 Java 为了实现 「一次编译,四处运行」 的目的,采用了一种稀奇的方案:先 编译与任何详细及其环境及操作系统环境无关的中心代码(也就是 .class 字节码文件),然后交由各个平台特定的 Java 注释器(也就是 JVM)来卖力 注释 运行。

编程职员和盘算机都无法直接读懂字节码文件,它必须由专用的 Java 注释器来注释执行,因此 Java 是一种在 编译基础上举行注释运行 的语言。(Java 程序运行流程如下)

「MoreThanJava」机器指令到汇编再到高级编程语言

Java 注释器 卖力将字节码文件翻译成详细硬件环境和操作系统平台下的机械代码,以便执行。因此 Java 程序不能直接运行在现有的操作系统平台上,它必须运行在被称为 Java 虚拟机的软件平台之上。

Java 虚拟机(JVM) 是运行 Java 程序的软件环境(我们后面会详细说到,这是学习 Java 绕不外的题),Java 注释器是 Java 虚拟机的一部分。在运行 Java 程序时,首先会启动 JVM,然后由它来卖力注释执行 Java 的字节码程序,而且 Java 字节码程序只能运行于 JVM 之上。这样行使 JVM 就可以把 Java 字节码程序和详细的硬件平台以及操作系统环境分开开来,只要在差异的盘算机上安装了针对特定平台的 JVM,Java 程序就可以运行,而不用思量当前详细的硬件平台及操作系统环境,也不用思量字节码文件是在何种平台上天生的。

JVM 把这种差异软、硬件平台的详细差异隐藏起来,从而 实现了真正的二进制代码级的跨平台移植。JVM 是 Java 平台架构的基础,Java 的跨平台特征正是通过在 JVM 中运行 Java 程序实现的。Java 的这种运行机制可以通过下图来说明:

「MoreThanJava」机器指令到汇编再到高级编程语言

Java 语言这种「一次编写,四处运行」的方式,有效地解决了现在大多数高级程序设计语言需要针对差异系统来编译发生差异机械代码的问题,即硬件环境和操作平台的异构问题,大大降低了程序开发、维护和治理的开销。

  • 提醒: Java 程序通过 JVM 可以实现跨平台特征,但 JVM 是不跨平台的。也就是说,差异操作系统之上的 JVM 是差异的,Windows 平台之上的 JVM 不能用在 Linux 平台,反之亦然。

参考资料

  1. Introduction to Computer Science using Java | CHAPTER 4 – http://programmedlessons.org/Java9/chap04/ch04_01.html
  2. 汇编语言入门教程 – http://www.ruanyifeng.com/blog/2018/01/assembly-language-primer.html
  3. CPU 是怎么熟悉代码的? | 知乎@Zign – https://www.zhihu.com/question/348237008/answer/843382847
  4. 改变天下的代码行 – https://www.infoq.cn/article/5CaYH8NbS6BmptWKRgkX
  5. The History of FORTRAN – https://www.obliquity.com/computer/fortran/history.html
  6. 《IT 通史》 | @李彦
  7. A Brief Totally Accurate History Of Programming Languages – https://medium.com/commitlog/a-brief-totally-accurate-history-of-programming-languages-cd93ec806124
  8. 编程语言分类 – https://www.cnblogs.com/nickchen121/p/10722720.html

往期精彩

  1. 「MoreThanJava」当大学选择了盘算机之后应该知道的
  2. 「MoreThanJava」盘算机发展史—从织布机到IBM
  3. 「MoreThanJava」盘算机系统概述
  4. 「MoreThanJava」一文领会二进制和CPU事情原理
  • 本文已收录至我的 Github 程序员发展系列 【More Than Java】,学习,不止 Code,迎接 star:https://github.com/wmyskxz/MoreThanJava
  • 小我私家民众号 :wmyskxz,小我私家自力域名博客:wmyskxz.com,坚持原创输出,下方扫码关注,2020,与您配合发展!

「MoreThanJava」机器指令到汇编再到高级编程语言

异常感谢列位人才气 看到这里,若是以为本篇文章写得不错,以为 「我没有三颗心脏」有点器械 的话,求点赞,求关注,求分享,求留言!

创作不易,列位的支持和认可,就是我创作的最大动力,我们下篇文章见!

原创文章,作者:28x29新闻网,如若转载,请注明出处:https://www.28x29.com/archives/13603.html