kvm启动流程

本文详细解析了KVM虚拟机的启动流程,从main方法入手,介绍了参数处理、堆大小设定、类路径配置等关键步骤。核心部分深入探讨了StartJVM方法,包括初始化ROM镜像、FPU、内存管理、类加载接口等组件,以及主类加载、参数解析和解释执行的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

从本文开始介绍,kvn的启动流程.

启动入口位于j2me_cldc/kvm/VmExtra/src/main.c的main方法.代码如下:

int main (int argc, char* argv[]) {
    int result;

#if USE_JAM
    char *jamInstalledAppsDir = "./instapps";
#endif

    JamEnabled = FALSE;
    JamRepeat = FALSE;
    RequestedHeapSize = DEFAULTHEAPSIZE; // 256*1024

#if ENABLE_JAVA_DEBUGGER
    suspend = TRUE;
#endif
    
    // 处理参数    
    while (argc > 1) {
            
        if (strcmp(argv[1], "-version") == 0) {
            fprintf(stdout, "Version: %s\n", BUILD_VERSION);
            exit(1);           
        } else if (strcmp(argv[1], "-help") == 0) {
            printHelpText();
            exit(0);

#if ENABLE_JAVA_DEBUGGER
        } else if (strcmp(argv[1], "-debugger") == 0) {
            debuggerActive = TRUE;
            argv++; argc--;
        } else if (strcmp(argv[1], "-suspend") == 0) {
            suspend = TRUE;
            argv++; argc--;
        } else if (strcmp(argv[1], "-nosuspend") == 0) {
            suspend = FALSE;
            argv++; argc--;
        } else if ((strcmp(argv[1], "-port") == 0) && (argc > 2)) {
            debuggerPort = (short)atoi(argv[2]);
            argv+=2; argc -=2;
#endif /* ENABLE_JAVA_DEBUGGER */

          // 设置堆大小
        } else if ((strcmp(argv[1], "-heapsize") == 0) && (argc > 2)) {
            char *endArg;
            long heapSize = strtol(argv[2], &endArg, 10);
            switch (*endArg) { 
                case '\0':            break;
                case 'k': case 'K':   heapSize <<= 10; break;
                case 'm': case 'M':   heapSize <<= 20; break;
                default:              printHelpText(); exit(1);
            }

          
            // 从经验上来看,KVM可以运行在只有几kb的堆内存上.将16k作为最小值.
            // KVM可以收集的最大内存为64m。实际上,GC只针对小堆进行了优化,并且如果堆大于几兆字节的话,很可能有明显的GC暂停。
            if (heapSize < 16 * 1024) { 
                fprintf(stderr, KVM_MSG_USES_16K_MINIMUM_MEMORY "\n");
                heapSize = 16 * 1024;
            }
            if (heapSize > 64 * 1024 * 1024) {
                fprintf(stderr, KVM_MSG_USES_64M_MAXIMUM_MEMORY "\n");
                heapSize = 64 * 1024 * 1024;
            }

            /*  确保堆的大小是4字节对齐的 */
            heapSize -= heapSize%CELL;
           
            argv+=2; argc -=2;
            RequestedHeapSize = heapSize;
            // 设置classpath
        } else if ((strcmp(argv[1], "-classpath") == 0) && argc > 2) {
            if (JamEnabled) {
                fprintf(stderr, KVM_MSG_CANT_COMBINE_CLASSPATH_OPTION_WITH_JAM_OPTION);
                exit(1);
            }
            UserClassPath = argv[2];
            argv+=2; argc -=2;

#if INCLUDEDEBUGCODE

#define CHECK_FOR_OPTION_IN_ARGV(varName, userName)  \
        } else if (strcmp(argv[1], userName) == 0) { \
            varName = 1;                             \
            argv++; argc--;                          

        FOR_EACH_TRACE_FLAG(CHECK_FOR_OPTION_IN_ARGV)

        } else if (strcmp(argv[1], "-traceall") == 0) {
#           define TURN_ON_OPTION(varName, userName) varName = 1;
            FOR_EACH_TRACE_FLAG(TURN_ON_OPTION)
            argv++; argc--;

#endif /* INCLUDEDEBUGCODE */

#if USE_JAM
        } else if (strcmp(argv[1], "-jam") == 0) {
            JamEnabled = TRUE;
            argv++; argc--;
        } else if (JamEnabled && (strcmp(argv[1], "-repeat") == 0)) {

#if ENABLE_JAVA_DEBUGGER
            if (debuggerActive) {
                fprintf(stderr,
                    KVM_MSG_CANT_COMBINE_DEBUGGER_OPTION_WITH_REPEAT_OPTION);
                exit(1);
            }
#endif /* ENABLE_JAVA_DEBUGGER */
            JamRepeat = TRUE;
            argv++; argc--;
        } else if (JamEnabled && (strcmp(argv[1], "-appsdir") == 0)
                  && argc > 2) {
            jamInstalledAppsDir = argv[2];
            argv+=2; argc-=2;
#endif /* USE_JAM */

        } else {
            break;
        }
    }

    /* Skip program name  跳过应用的名称*/
    argc--;
    argv++;

    // 设置classpath
    if (UserClassPath == NULL && !JamEnabled) { 
        UserClassPath = getenv("classpath");
        if (UserClassPath == NULL) { 
            /* Just in case environment variable reading is case sensitive */
            UserClassPath = getenv("CLASSPATH");
            /* Use "." as the default classpath */
            if (UserClassPath == NULL) { 
                UserClassPath = ".";
            }
        }
    }

#if USE_JAM
    if (JamEnabled) {
        if (argc != 1 ) {
            fprintf(stderr, 
                    KVM_MSG_EXPECTING_HTTP_OR_FILE_WITH_JAM_OPTION);
            exit(1);
        }
        JamInitialize(jamInstalledAppsDir);
        do {
            result = JamRunURL(argv[0], JamRepeat);
            if (result == JAM_RETURN_ERR) {
                break;
            }
        } while(JamRepeat);
        JamFinalize();
    } else
#endif /* USE_JAM */

    {
 
        /*  启动kvm虚拟机*/
        result = StartJVM(argc, argv);

#if ENABLEPROFILING
        /* By default, the VM prints out profiling information */
        /* upon exiting if profiling is turned on.  打印性能参数*/
        printProfileInfo();
#endif /* ENABLEPROFILING */

        /* If no classfile was provided, print help text  如果没有类文件提供的话,打印帮助信息*/
        if (result == -1) printHelpText();
    }

    return result;
}

这处的方法比较简单.步骤如下:

  1. 通过循环,依次处理参数.其中,对于-heapsize而言,其最小为16k,最大为64m.
  2. 设置classpath
  3. 调用StartJVM,启动kvm虚拟机.
  4. 如果没有类文件提供的话,打印帮助信息.

这里重要的是第3步.

StartJVM

此部分的代码如下:

int StartJVM(int argc, char* argv[])
{
    volatile int returnValue = 0;

    /*  1.必须提供要运行的类名,如果没提供的话,返回-1 */
    if (argc <= 0 || argv[0] == NULL) {
        AlertUser(KVM_MSG_MUST_PROVIDE_CLASS_NAME);
        return -1;
    }

    // 2. 启动KVM
    returnValue = KVM_Start(argc, argv);
    // 3. 回收资源
    KVM_Cleanup();
    return returnValue;
}

KVM_Start

此处的代码如下(经过宏展开后的结果):

int KVM_Start(int argc, char* argv[])
{
    ARRAY arguments;
    INSTANCE_CLASS mainClass = NULL;
    volatile int returnValue = 0; /* Needed to make compiler happy */

                                                               
        struct throwableScopeStruct __scope__;                 
        int __state__;                                         
        jmp_buf __env__;                                       
        __scope__.outer = ThrowableScope;                      
        ThrowableScope = &__scope__;                          
        ThrowableScope->env = &__env__;                        
        ThrowableScope->tmpRootsCount = TemporaryRootsLength;  
        ASSERT_NO_ALLOCATION_GUARD                             
        TRACE_EXCEPTION("TRY")                                 
        if ((__state__ = setjmp(__env__)) == 0) { 
           
                 		                                                          
		        jmp_buf __env__;                                       
		        VMScope = &(__env__);                                  
		        if (setjmp(__env__) == 0) {
		           /* 1. 创建ROM镜像*/
		            CreateROMImage();
		
		            /* 2. 初始化FPU*/
		            InitializeFloatingPoint();
		
		#if ASYNCHRONOUS_NATIVE_FUNCTIONS
		            /* 3.初始化异步I/0系统*/
		            InitalizeAsynchronousIO();
		#endif
		
		            /* 4. 初始化本地代码 */
		            InitializeNativeCode();
		            InitializeVM();// 5. 初始化vm
		
		            /* 6.初始化全局变量 */
		            InitializeGlobals();
		
		            /* 7. 初始化性能统计的变量 */
		            InitializeProfiling();
		
		            /* 8. 初始化内存系统 */
		            InitializeMemoryManagement();
		
		            /* 9. 初始化内部hash 表 */
		            InitializeHashtables();
		
		            /* 10.初始化inline cache */
		            InitializeInlineCaching();
		
		            /* 11. 初始化类加载接口*/
		            InitializeClassLoading();
		
		            /* 12. 初始化VM所需要的内部类*/
		            InitializeJavaSystemClasses();
		
		            /* 13. 初始化class文件验证器 */
		            InitializeVerifier();
		
		            /* 14.  初始化事件处理系统 */
		            InitializeEvents();
		
		       
		            /* 15. 加载主类 */
		            mainClass = loadMainClass(argv[0]);
		
		            /*  16. 解析参数 */
		            arguments = readCommandLineArguments(argc - 1, argv + 1);
		
		            /* 17. 初始化多线程 */
		            InitializeThreading(mainClass, arguments);
		
		#if ENABLE_JAVA_DEBUGGER
		            /* Prepare the VM for Java-level debugging */
		            if (debuggerActive) {
		                InitDebugger();
		            }
		#endif
		            /* 
		             * 18. 初始化系统类
		             */
		            initializeClass(JavaLangOutOfMemoryError);
		            initializeClass(JavaLangSystem);
		            initializeClass(JavaLangString);
		            initializeClass(JavaLangThread);
		            initializeClass(JavaLangClass);
		
		#if ENABLE_JAVA_DEBUGGER
		            /* Prepare the VM for Java-level debugging */
		            if (vmDebugReady) {
		                setEvent_VMInit();
		                if (CurrentThread == NIL) {
		                    CurrentThread = removeFirstRunnableThread();
		                    /* Make sure xp_globals are synched with thread data */
		                    loadExecutionEnvironment(CurrentThread);
		                }
		                sendAllClassPrepares();
		            }
	
		
		            /* 19.解释执行 */
		            Interpret();
		     
				  
			     } else {                                               
            		int value = VMExitCode; 
		          returnValue = value;
				            
				  }                                                      
    	
     
    		} 
    		
    		                                                     
        TRACE_EXCEPTION("CATCH")                               
        ThrowableScope = __scope__.outer;                      
        TemporaryRootsLength = __scope__.tmpRootsCount;        
        if (__state__ != 0) {                                  
            START_TEMPORARY_ROOTS                              
                 DECLARE_TEMPORARY_ROOT(THROWABLE_INSTANCE,    
                     e,__scope__.throwable);
                     
                      
								        /* Any uncaught C-level exception above will transfer control here  在之前c语言级别的异常没有捕获的,都在这里处理*/
								        if (mainClass == NULL) {
								            /* If main class was not found, print special error message 如果主类没有找到的话,打印特殊的异常 */
								            char buffer[STRINGBUFFERSIZE];
								
								            sprintf(buffer, "%s", getClassName((CLASS)e->ofClass));
								            if (e->message != NULL) {
								                sprintf(buffer + strlen(buffer),": %s", getStringContents(e->message));
								            }
								            sprintf(str_buffer, "%s", buffer);
								            AlertUser(str_buffer);
								            returnValue = 1;
												} else {
										            Log->uncaughtException(e);
										            returnValue = UNCAUGHT_EXCEPTION_EXIT_CODE;
												}
    								  TemporaryRootsLength = _tmp_roots_;                                
        }                                                      
    

    return returnValue;
}

该方法的步骤如下:

  1. 创建ROM镜像,此处为宏,定义在j2me_cldc/kvm/VmCommon/h/garbage.h
  2. 初始化FPU
  3. 初始化异步I/0系统
  4. 初始化本地代码
  5. 初始化vm,此处为宏,定义在j2me_cldc/kvm/VmUnix/h/machine_md.h
  6. 初始化全局变量
  7. 初始化性能统计的变量,定义在 j2me_cldc/kvm/VmCommon/h/profiling.h
  8. 初始化内存系统
  9. 初始化内部hash 表
  10. 初始化inline cache
  11. 初始化类加载接口
  12. 初始化VM所需要的内部类
  13. 初始化class文件验证器
  14. 初始化事件处理系统
  15. 加载主类
  16. 解析参数
  17. 初始化多线程
  18. 初始化系统类
  19. 解释执行

初始化FPU

此处的代码如下:

void InitializeFloatingPoint() {
#if defined(LINUX) && PROCESSOR_ARCHITECTURE_X86
    /* Set the precision FPU to double precision  设置FPU为双精度*/
	// 0x037f & ~0x300 | 0x200 
    fpu_control_t cw = (_FPU_DEFAULT & ~_FPU_EXTENDED) | _FPU_DOUBLE;
    _FPU_SETCW(cw); // fldcw cw  --> 设置状态寄存器
#endif
}

此处是通过修改fpu 的状态寄存器将其修改为双精度.关于此处可以参考如下链接:

初始化异步I/0系统

此处的代码为:



int VersionOfTheWorld = 0;

void InitalizeAsynchronousIO(void) {
    if (VersionOfTheWorld++ == 0) {
        int i;
        for (i = 0 ; i < ASYNC_IOCB_COUNT ; i++) { // ASYNC_IOCB_COUNT = 5
            ASYNCIOCB *aiocb = &IocbRoots[i];
            FreeAsyncIOCB(aiocb);
        }
    } else {
        while (ActiveAsyncOperations() > 0) {
            Yield_md();
        }
    }
}

由于VersionOfTheWorld = 0 ,因此此处会进行异步I/O的初始化.通过循环,调用FreeAsyncIOCB 进行初始化.

此处使用了ASYNCIOCB,其代码如下:

ASYNCIOCB  IocbRoots[ASYNC_IOCB_COUNT];
ASYNCIOCB *IocbFreeList = 0;

typedef struct asynciocb {
    struct asynciocb   *nextFree;
    THREAD              thread;
    INSTANCE            instance;
    BYTEARRAY           array;
    char               *exception;
} ASYNCIOCB;

由于ASYNC_IOCB_COUNT默认为5,因此实例化了长度为5的ASYNCIOCB数组,其中ASYNCIOCB又通过nextFree想连,形成链表.如图所示:

在这里插入图片描述

这里使用了FreeAsyncIOCB,代码如下:

static void FreeAsyncIOCB(ASYNCIOCB *aiocb) {
    aiocb->thread       = 0;
    aiocb->instance     = 0;
    aiocb->array        = 0;
    aiocb->exception    = 0;
    aiocb->nextFree     = IocbFreeList;
    IocbFreeList        = aiocb;
}

注意,在该方法中使用了头插法.

初始化本地代码

此处使用的是InitializeNativeCode,代码如下:

void InitializeNativeCode() {
    /* 
     * 用来处理dump stack
     */
    signal(SIGILL,  signal_handler); // 执行了非法指令, 通常是因为可执行文件本身出现错误,或者试图执行数据段, 堆栈溢出时也有可能产生这个信号。
    signal(SIGABRT, signal_handler); // 异常终止条件,例如 abort() 所起始的
    signal(SIGBUS,  signal_handler); // 非法地址,包括内存地址对齐(alignment)出错。比如访问一个四个字长的整数,但其地址不是4的倍数。它与SIGSEGV的区别在于后者是由于对合法存储地址的非法访问触发的(如访问不属于自己存储空间或
    signal(SIGSEGV, signal_handler); // 非法内存访问(段错误)
    signal(SIGPIPE, SIG_IGN); // 管道破裂。这个信号通常在进程间通信产生,比如采用FIFO(管道)通信的两个进程,读管道没打开或者意外终止就往管道写,写进程会收到SIGPIPE信号。此外用Socket通信的两个进程,写进程在写Socket的时候,读进程已经终止
}

初始化全局变量

此处的代码如下:

//  This flag indicates whether class loading has been  initiated from Class.forName() or from elsewhere in the virtual machine
此标志指示类加载是从class.forname()还是从虚拟机中的其他位置启动的
bool_t loadedReflectively;

void InitializeGlobals(void)
{
    loadedReflectively = FALSE;
}

后续流程,下文讲述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值