找回密码
 注册
快捷导航
查看: 14578|回复: 20

【原创】KVM源代码阅读

[复制链接] |自动提醒
阅读字号:

627

回帖

41

积分

310

资产值

钻石会员 Rank: 3Rank: 3Rank: 3

注册时间
2002-7-3
铜牌荣誉勋章(注册8年以上会员)银牌荣誉勋章(注册10年以上会员)
发表于 2008-2-24 00:36:01| 字数 989| - 中国–江苏–南京 电信/(建邺区/白下区)电信 | 显示全部楼层 |阅读模式
(我先说一下写这个帖子的目的:
在嵌入式设备上为什么使用KJAVA,而不直接使用C编程。
KJAVA设计的目的就是为有限的硬件设备上提供一个方便的编程接口,比C方便许多,尤其是在对内存的管理上(尽管目前很多KJAVA平台的内存管理还是不尽人意)。最明显的例子是:手机上,用C写的应用才多少,而用KJAVA写的应用数量有多庞大,两者根本不是一个数量级别的差异。
目前linux在手机OS领域突飞猛进,而基于linux的KJAVA或者Andriod(Google领导开发的)开发将变得更为活跃


KVM,本意:Kilobytes Virtual Mechine,形象点,可以称作Kjava Virtual Mechine
是SUN当年为移动设备(大多是手机)开发的一套小型JAVA虚拟机。

KVM的代码后来被SUN开源了,但这个KVM的源代码只能算一个原型代码,并不是大家手机上目前使用的KJAVA虚拟机,几个大的手机厂商,如Nokia,Motorola等都在这个基础上开发了自己的KVM,包括IBM为PDA开发的J9。
这些KVM虽然实现的都不同,但其核心原理和大体的代码不会离SUN的KVM原型代码差多远,毕竟他们都要符合KVM Spec。

我阅读这套原型代码的目的不是为了工作,只是想在一些嵌入/移动设备上能跑我自己写的KJAVA程序,比如作个能走出迷宫的机器人。

KVM的代码是C写的,它的部分实现必须依赖于底层操作系统。所以KVM的代码结构上分成了:
Common
Extra
Unix/Linux
Win/WinCE
其中Common和Extra是ANSI的代码部分,意味着在所有OS平台上都能正常编译运行,而依赖OS部分代码被分别放在Unix/Linx/Win/WinCE里了。

而以上其实只包括了KVM中CLDC(Connected Limited Device Configuration)的部分。KJAVA的本意是将KVM内部再切分为两块:CLDC和MIDP(Mobile Information Device Profile),其中CLDC是核心。

KVM在完成了对JAVA字节码(也就是大家写的JAVA程序被编译后的码,称作bytecode)的解释执行以及JAVA核型包:
java.lang后,也就基本做到了CLDC的核心功能定义。

(待续)

[ Edited by  Roamvalley on 2008-2-24 03:13 ]
T410S

627

回帖

41

积分

310

资产值

钻石会员 Rank: 3Rank: 3Rank: 3

注册时间
2002-7-3
铜牌荣誉勋章(注册8年以上会员)银牌荣誉勋章(注册10年以上会员)
 楼主| 发表于 2008-2-24 00:44:24| 字数 285| - 中国–江苏–南京 电信/(建邺区/白下区)电信 | 显示全部楼层
可以这么说,在不考虑
java.io和java.util的前提下,java.lang已经能完成很多核心功能了,是不是java.lang很简单呢?
NO
看看java.lang里包括的类吧,其中:
java.lang.Thread
就是KVM中比较复杂的代码部分了。想想也能意识到,线程这个东西是那么容易实现的吗?

可能有不少JAVA初学者对各种package如java.lang,java.io和java.util等等同底层的JAVA虚拟机关系搞不明白,它们究竟是什么关系?我们写的JAVA和它们又是什么关系?

[ Edited by  Roamvalley on 2008-2-24 00:46 ]
T410S
回复 支持 反对

使用道具 举报

627

回帖

41

积分

310

资产值

钻石会员 Rank: 3Rank: 3Rank: 3

注册时间
2002-7-3
铜牌荣誉勋章(注册8年以上会员)银牌荣誉勋章(注册10年以上会员)
 楼主| 发表于 2008-2-24 01:11:00| 字数 610| - 中国–江苏–南京 电信/(建邺区/白下区)电信 | 显示全部楼层
在这里我作个比较:
(Hardware/OS) +C Lib + C Compiler + C Application
KVM + JAVA Lib + JAVA Compiler + Java Application

大致能表达出经典的C程序和JAVA程序所涉及的范围。为什么要列出上面这个关系对比,是因为在后面阅读KVM代码时,有些概念和框架必须一开始就明确,而不是稀里糊涂的就一头埋到代码里去了。

1。这里可以看出:KVM = Hardware/OS。就是说KVM的核心是对硬件(主要是CPU)的模拟。

2。C Lib是什么?它向C程序员提供了最基础的(AnisC)的所有函数,最直观的感受你写C代码时import的那些头文件。
JAVA lib是什么?可以当作你写JAVA代码时import的那些package。
大致有些感受了吧
C lib和JAVA lib是必须的吗?
No!
看看下面的C代码和JAVA代码
C:
            int plus(int _input)
            {
                 return _input++;
            }

JAVA:
            public int plus(int _input)
            {
                 return _input++;
             }
如果大家写的代码就上面这几行,我们还需要去import任何lib吗?根本没必要,因为这几行代码属于“语言自身的能力”,C Compile和JAVA Compiler直接把上面代码编译成"CPU"指令就可以执行了。

[ Edited by  Roamvalley on 2008-2-24 03:08 ]
T410S
回复 支持 反对

使用道具 举报

627

回帖

41

积分

310

资产值

钻石会员 Rank: 3Rank: 3Rank: 3

注册时间
2002-7-3
铜牌荣誉勋章(注册8年以上会员)银牌荣誉勋章(注册10年以上会员)
 楼主| 发表于 2008-2-24 01:19:22| 字数 318| - 中国–江苏–南京 电信/(建邺区/白下区)电信 | 显示全部楼层
为什么上面会讨论到lib的这些细节,因为后面说到KVM代码时,我们会发现KVM对“普通执行码”也就是最接近CPU指令的那些代码是一种处理方式,而另一些被import的代码部分是以另外的方式处理的。

3。CCompiler:GCC
     JAVAcompiler:JAVAC
它们把程序员写的C和JAVA代码翻译为“机器码”。
JAVA Compiler并不属于KVM,但阅读KVM代码时如果不理解JAVAC的工作机制可以说是步步为艰了。因为JAVAC几乎为KVM预先做好了一半的工作。但对JAVAC,我们并不需要去了解它的内部代码和实现,只要关心的它的实现规范,也就是 Class Bytecodes Spec就可以了,也就是大家编译后的Class文件内容。
T410S
回复 支持 反对

使用道具 举报

627

回帖

41

积分

310

资产值

钻石会员 Rank: 3Rank: 3Rank: 3

注册时间
2002-7-3
铜牌荣誉勋章(注册8年以上会员)银牌荣誉勋章(注册10年以上会员)
 楼主| 发表于 2008-2-24 01:26:21| 字数 144| - 中国–江苏–南京 电信/(建邺区/白下区)电信 | 显示全部楼层
4我就不说了。

通过前面的叙述,逐渐可以看出,阅读KVM的代码,需要具备以下几点最基础的知识:
CPU(硬件)的运作机制,学过汇编的应该都大致了解
内存管理(OS)
进程/线程原理(OS)
其它的,比如类加载机制,类检验机制等KVM所特有的(区别于一般虚拟机概念)功能后面看时间的允许在具体谈到了。
T410S
回复 支持 反对

使用道具 举报

627

回帖

41

积分

310

资产值

钻石会员 Rank: 3Rank: 3Rank: 3

注册时间
2002-7-3
铜牌荣誉勋章(注册8年以上会员)银牌荣誉勋章(注册10年以上会员)
 楼主| 发表于 2008-2-24 01:50:50| 字数 300| - 中国–江苏–南京 电信/(建邺区/白下区)电信 | 显示全部楼层
进入KVM代码部分了。

作任何事情,我们都希望知道怎么开头的,KVM代码看起来很复杂,但有几根主线,我们顺着线头把它们拎起来理清楚,就能了解它大致的工作机制了。

当我们写好的JAVA程序被编译成CLASS字节码后,要使用KVM将它加载到内存里运行起来,首先,KVM自身要作一个初始化工作,这段代码位于:
./common/StartJVM.c
中,这个文件的自我描述是:
System initialization and start of JVM

关键函数:
KVM_Start
{
           初始化OS相关数据
           初始化内存管理
           初始化事件管理
           加载用户类
           解析虚拟机初始参数
           初始化线程管理
           初始化Java Lib
           将执行权转交JAVA字节码解释器
T410S
回复 支持 反对

使用道具 举报

627

回帖

41

积分

310

资产值

钻石会员 Rank: 3Rank: 3Rank: 3

注册时间
2002-7-3
铜牌荣誉勋章(注册8年以上会员)银牌荣誉勋章(注册10年以上会员)
 楼主| 发表于 2008-2-24 02:09:55| 字数 223| - 中国–江苏–南京 电信/(建邺区/白下区)电信 | 显示全部楼层
学过编程的大都知道,如果上面的KVM_Start就这么从上到下执行掉了,意味着加载了用户的代码后程序就退出了。
其实看一下这行:
将执行权转交JAVA字节码解释器
这调用了另一个重要文件里的核心函数:
execute.c
里的Interpret(),而这个函数真正的实现体是:
FastInterpret()
它是一个循环体,意味着从KVM_Start过来后,这里就不断循环在解释执行用户代码。也就是这里涉及到了KVM的两个关键功能:
1。线程管理
2。执行字节码
T410S
回复 支持 反对

使用道具 举报

627

回帖

41

积分

310

资产值

钻石会员 Rank: 3Rank: 3Rank: 3

注册时间
2002-7-3
铜牌荣誉勋章(注册8年以上会员)银牌荣誉勋章(注册10年以上会员)
 楼主| 发表于 2008-2-24 02:28:34| 字数 757| - 中国–江苏–南京 电信/(建邺区/白下区)电信 | 显示全部楼层
在看KVM源码前,我一直没搞明白一件事:
字节码执行器和线程的关系。
究竟是字节码执行器拥有所有线程对象并分配每个线程的执行时间片,还是线程对象拥有字节码执行器并在自己的时间片中调用字节码执行器去解释此线程内的代码段?

看了execute.c中的
SlowInterpret()和FastInterpret()我终于明白了:
1。KVM中的线程分为内核线程和JAVA线程两个概念,我们通常在写JAVA程序时接触的是JAVA线程
2。字节码执行器拥有主控制权,它从代码级对线程的调度作控制。

解释这两点:
1。KVM为什么要把线程搞成两个,不是变复杂了么?
从系统设计的角度来说,清晰的概念将决定清晰可读的代码,KVM的核心工作职责是对字节码的解释执行,如果把JAVA Lib这个层次上的线程(java.lang.Thread)同这个核心工作混杂在一个级别那就是概念不清晰。
但KVM内部又的确是需要对线程作管理的,这就基本上导致系统内出现两种线程,一个是靠近KVM的核心线程,一个是向上为JAVA程序员提供的JAVA线程。
这两个线程在KVM代码内都有相应结构(C strcuct):
位于文件thread.h中
struct threadQueue:核心线程

     .....
     运行时间片
     javaThreadStruct* javaThread;
     .......
  }
struct javaThreadStruct:面向JAVA开发人员的线程
{
     .....
}

可以看出,核心线程拥有指向JAVA线程的指针。
KVM的字节码执行器(excute.c)就是通过这两个结构了解每个线程的生命周期和各种属性,
interpret()正是通过threadQueue.运行时间片了解每个线程占用“CPU”的状况,以此决定是否要切换到另一个等待线程上还是继续运行这个线程里其它的代码段
T410S
回复 支持 反对

使用道具 举报

627

回帖

41

积分

310

资产值

钻石会员 Rank: 3Rank: 3Rank: 3

注册时间
2002-7-3
铜牌荣誉勋章(注册8年以上会员)银牌荣誉勋章(注册10年以上会员)
 楼主| 发表于 2008-2-24 02:55:25| 字数 453| - 中国–江苏–南京 电信/(建邺区/白下区)电信 | 显示全部楼层
用一段伪代码我描述一下KVM内线程的管理机制:

void interpret()
{
     while(!quitFlag)
     {
            if(当前线程的时间片是否全部使用完)
            {
                 if(有其它等待线程)
                 {
                    运行其它等待线程的代码段;
                 }
                 else
                 {
                    继续执行当前线程的代码段
                 }
             }
             else
             {
                 当前线程的时间片--;
                 继续执行当前线程的代码段;
             }
      }
}

可以看出,KVM内的线程是假线程,并不是真正的OS线程。
KVM和JVM面向不同的硬件环境的,JVM是我们通常在服务器和桌面运行的标准JAVA虚拟机,它不可能用这种假线程,因为目前PC/Server大多有多核或者多颗CPU,最适合运行多进程和多线程的程序了(包括操作系统自身),JVM的源代码我没看过,但肯定把JAVA的线程实现映射到了真实OS线程或进程上,那么JVM也就不会出现上面这样的伪代码实现机制。
毕竟KVM面向的移动设备大多是Symbian,嵌入式linux系统或者MTK,它们在有限的硬件上能否实现OS级线程都很难说。

[ Edited by  Roamvalley on 2008-2-24 02:56 ]
T410S
回复 支持 反对

使用道具 举报

1595

回帖

0

积分

1753

资产值

入门会员 Rank: 1

注册时间
2007-10-28
发表于 2008-2-24 04:35:49| 字数 23| - 中国–广东–深圳 电信 | 显示全部楼层
看来是写完了 呵呵  看了好久直播---不敢露头打断
蓝快里高手如云,垃圾如星,仰望天空,万里无云,群星璀璨
回复 支持 反对

使用道具 举报

627

回帖

41

积分

310

资产值

钻石会员 Rank: 3Rank: 3Rank: 3

注册时间
2002-7-3
铜牌荣誉勋章(注册8年以上会员)银牌荣誉勋章(注册10年以上会员)
 楼主| 发表于 2008-2-24 12:52:40| 字数 33| - 中国–江苏–南京 电信/(建邺区/白下区)电信 | 显示全部楼层
没写完,昨晚太困了。
对照代码量,上面差不多涉及到1/6功能的源码。
T410S
回复 支持 反对

使用道具 举报

627

回帖

41

积分

310

资产值

钻石会员 Rank: 3Rank: 3Rank: 3

注册时间
2002-7-3
铜牌荣誉勋章(注册8年以上会员)银牌荣誉勋章(注册10年以上会员)
 楼主| 发表于 2008-2-24 13:34:54| 字数 829| - 中国–江苏–南京 电信/(建邺区/白下区)电信 | 显示全部楼层
KVM的字节码执行方式:

非常类似CPU处理机器指令的过程(KVM本来就是要模拟这个环节的),它们最大的区别在于对寄存器的使用。
程序员在编写汇编代码的时候,是很清楚如何控制CPU的所有寄存器。KVM因为考虑低端的硬件设备,有些CPU只有很少的几个寄存器,那么KVM在被设计的时候就不能被随便的设计为:支持具备N个寄存器的CPU。
KVM对此的解决方法是用“栈”来实现这个功能,汇编中指令的操作数大多存在寄存器中,运算的结果通常也放在寄存器中,KVM把这些都放在了“栈”中。它被称为"操作栈“(Operand Stack),是个标准的FIFO。
这里举个使用"栈"来处理字节码的例子:
IADD:整数相加。
在汇编中,我们通常将加数和被加数以及结果都放到寄存器中:
ADD AX,BX      //(AX+BX -> AX)

而KVM中,IADD的做法是:

int tmp=popStack(); //从"栈"中取被加数
*(int *SP)+=tmp; //从"栈"中取加数并和tmp相加,结果仍存到加数所在的"栈“地址,其实就是栈顶了
以上的popStack和SP都是"栈"操作

上面描述了KVM对"寄存器"概念的独特设计方法。
从整体来看,字节码的执行操作大致如下(以单线程为参考)

void interpret()
{
     while(!quitFlag)
     {
        int operand = *ip;//从代码区取当前指令
        swich(operand)
        {//通过指令类别分别处理
              case IADD:
                    int tmp=popStack();
                    *(int *SP)+=tmp;
                    break;
              case ISUB:
                     ............
                     break;
              ....................
              default:
                      .........
                     break;
         }
        ip++;
     }
}

[ Edited by  Roamvalley on 2008-2-24 13:47 ]
T410S
回复 支持 反对

使用道具 举报

627

回帖

41

积分

310

资产值

钻石会员 Rank: 3Rank: 3Rank: 3

注册时间
2002-7-3
铜牌荣誉勋章(注册8年以上会员)银牌荣誉勋章(注册10年以上会员)
 楼主| 发表于 2008-2-24 13:56:30| 字数 647| - 中国–江苏–南京 电信/(建邺区/白下区)电信 | 显示全部楼层
说道字节码,可能还是没法让我们认识到JAVA Lib层的实现方式,其实这个问题也是大部分研究KVM的人最关心的事:
如果搞明白了JAVA Lib是怎么实现的,就可以在KVM中添加自己需要的新功能,比如让KVM支持USB!那样就可以在KJAVA里写程序来控制一些USB设备了。

回到JAVA代码中,我们先写几行代码:
public class test

      public void testFunc()
      {
          abc();
      }


这里的abc()可以看作调用test类的某个成员函数,或者是调用java.lang中某个类的成员函数。
那么在经过JavaCompiler编译后这段代码变成了什么样的字节码呢?大致如下:

Method testFunc()
0 aload_0
1 invokespecial #5 <Method abc()>
return

"0 aload_0"这个先别管了(将test intance自身入"栈"),看下面那行
1 invokespecial #5 <Method abc()>

这个“invokespecial“就是一个KVM指令,地位上等同于上面的IADD,它要干嘛?它执行一段字节码就了事了?
其实它要调用另外一个非常重要的代码段来完成对“JAVA级别的函数”调用,而不是停留在字节码这个层次上。
callMethod_general
{
      ........
}

真是这个代码段做到对abc()的实际调用。

[ Edited by  Roamvalley on 2008-2-24 14:11 ]
T410S
回复 支持 反对

使用道具 举报

627

回帖

41

积分

310

资产值

钻石会员 Rank: 3Rank: 3Rank: 3

注册时间
2002-7-3
铜牌荣誉勋章(注册8年以上会员)银牌荣誉勋章(注册10年以上会员)
 楼主| 发表于 2008-2-24 14:19:57| 字数 384| - 中国–江苏–南京 电信/(建邺区/白下区)电信 | 显示全部楼层
java.lang
中的类,从某种意义上来讲,它相当于C Lib里的头文件,却不是真实的lib。
它的实现体位于KVM中,更具体的说,位于:
nativeCore.c
这个文件里,那么我们调用java.lang的过程是怎么回事?
上面那段代码里谈到callMethod_general时就差不多涉及到我们的问题核心了。
callMethod_general会查看你是否调用的java.lang内功能,如果是,它将此调用映射到
nativeCore.c
中,通过nativeCore.c内对java.lang的具体实现来达到目的。

到这里为止,已经基本了解
(Hardware/OS) +C Lib + C Compiler + C Application
KVM + JAVA Lib + JAVA Compiler + Java Application
这个对比的含义了。我们能通过KVM的代码“感受”到各个阶段和模块的用意。
T410S
回复 支持 反对

使用道具 举报

627

回帖

41

积分

310

资产值

钻石会员 Rank: 3Rank: 3Rank: 3

注册时间
2002-7-3
铜牌荣誉勋章(注册8年以上会员)银牌荣誉勋章(注册10年以上会员)
 楼主| 发表于 2008-2-24 14:26:17| 字数 332| - 中国–江苏–南京 电信/(建邺区/白下区)电信 | 显示全部楼层
那么其它的java package是否也是这么实现的?
不是,尽管 java.io和java.util内某些功能仍依赖于在KVM中实现,但不是全部。
比如Vector这个类,它本来是可以在KVM里去作,但那样nativeCore.c会变得很复杂。
其实只要java.lang完成了,那么java.util.Vector就可以用纯粹的JAVA来实现,而不是用C。

大家在安装好JDK后,可以看看JDK根目录下有个src.zip这个文件,解压后可以看到,很多java的package都根本就是用JAVA写的了,根本不涉及到JVM那么底层。那是因为
java.lang
java.io
java.util
3大基础package有了后,它们已经能够“组装”出更多其它功能的package了。
T410S
回复 支持 反对

使用道具 举报

627

回帖

41

积分

310

资产值

钻石会员 Rank: 3Rank: 3Rank: 3

注册时间
2002-7-3
铜牌荣誉勋章(注册8年以上会员)银牌荣誉勋章(注册10年以上会员)
 楼主| 发表于 2008-2-24 14:30:33| 字数 105| - 中国–江苏–南京 电信/(建邺区/白下区)电信 | 显示全部楼层
暂时告一段落。还有不少重要的内容没说道:
常量池(ContantsPool)
垃圾回收器(GarbageCollector)
JAVA栈(Java Stack)

但KVM的运作机制大体是写了,其它的等有时间了我再补充。
T410S
回复 支持 反对

使用道具 举报

1797

回帖

24

积分

4612

资产值

白金会员 Rank: 3Rank: 3Rank: 3

注册时间
2004-4-6
发表于 2008-2-24 20:32:15| 字数 25| - 中国–广东–广州–天河区 电信/黄埔区电信 | 显示全部楼层
学习,看过以前同事写的自己的编译器和虚拟机,屁服。
X201
回复 支持 反对

使用道具 举报

627

回帖

41

积分

310

资产值

钻石会员 Rank: 3Rank: 3Rank: 3

注册时间
2002-7-3
铜牌荣誉勋章(注册8年以上会员)银牌荣誉勋章(注册10年以上会员)
 楼主| 发表于 2008-2-24 20:58:48| 字数 162| - 中国–江苏–南京 电信 | 显示全部楼层

回复 #17 vinson_zeng 的帖子

我不知道学校里教授编译原理这门课是怎么作的(我不是学软件相关专业的),按照编译原理的课本看来,学完后应该作一个简单的编译器和相应的虚拟机作为毕业设计才对。
而且现在对语法分析和虚拟机都有相应的辅助工具,在明白编译原理的基础上使用这些工具和自己编写代码来实现一个编译器和虚拟机不是非常复杂的事情,除非作一个有实际用途的完整产品。
T410S
回复 支持 反对

使用道具 举报

1

回帖

0

积分

13

资产值

入门会员 Rank: 1

注册时间
2009-5-9
发表于 2009-5-9 23:01:17| 字数 13| - 中国–陕西–西安 电信 | 显示全部楼层
请问一下在哪可以下到源码?
回复 支持 反对

使用道具 举报

5109

回帖

78

积分

1万

资产值

至尊会员I Rank: 4Rank: 4Rank: 4Rank: 4

注册时间
2007-11-17
发表于 2009-5-19 15:56:38| 字数 29| - 中国–江苏–南京 电信 | 显示全部楼层
嗨 我是学应用软件 跟这些底层高深原理一点关系都没有
看也看不懂
51nb才是正宗的 nb论坛
51nb才是正宗的 nb论坛
51nb才是正宗的 nb论坛
T420
E71 lx5
php coder
回复 支持 反对

使用道具 举报

887

回帖

22

积分

3741

资产值

白金会员 Rank: 3Rank: 3Rank: 3

注册时间
2004-5-9
发表于 2009-5-25 09:20:52| 字数 22| - 中国–辽宁–沈阳 联通 | 显示全部楼层
不错哦,有没有完整的版本啊?down下来收藏
t410+双显卡
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

Powered by Discuz! X3.5 © 2001-2023 Comsenz Inc

GMT+8, 2025-1-24 05:04 , Processed in 0.174779 second(s), 57 queries , Gzip On, OPcache On.

手机版|小黑屋|安卓客户端|iOS客户端|Archiver|备用网址1|备用网址2|在线留言|专门网

返回顶部