The PersonalJava virtual machine executes class files and manages native methods for Java software.
The source code for the PersonalJava virtual machine is derived from the JDK 1.1 virtual machine, though it has been optimized for use in memory constrained environments. Like most of the PJAE, the source code for the virtual machine is divided into platform-independent and platform-dependent parts. The porting effort is concentrated on the implementation of a group of functions in a small part of the platform-dependent source code in platform/java/runtime.
The virtual machine portability layer is heavily based on the POSIX Open System Environment and the ANSI C Library. If the target RTOS has POSIX and ANSI C libraries, then porting the PersonalJava virtual machine is straightforward. If not, then the porting effort is based around developing pseudo POSIX and ANSI C libraries.
Most of the effort in porting the PersonalJava virtual machine is centered on getting it running and then tuning its performance. The PersonalJava licensee does have a few other choices to make:
The diagram below provides an overview of the PersonalJava virtual machine architecture. The diagram is divided into four layers:
It helps to spend some time becoming familiar with the sample implementations. While they are functionally equivalent, they serve different purposes. The solaris port is more useful as an implementation example because it is similar to most target RTOSs. The win32 port can be used to build versions of the PJEE for application development on PCs.
The figure below illustrates the initial startup sequence
for the PersonalJava virtual machine.
The sequence is started by a platform-dependent invocation tool,
kept in build/platform/bin.
This launches the Java interpreter which is divided into
a small amount of platform-dependent source code
in src/platform/java/javai
or src/platform/java/main.
This platform-dependent code
then launches the rest of the Java interpreter
which executes the main
method
in the application's top-level class.
The following list of steps represents the major tasks to perform during the initial port of the PersonalJava virtual machine:
Here are two useful tips for developing the HPI functions:
The HPI functions are described in the tables below. They are organized by category and identified by their source files.
The sample implementations treat thread packages differently. The win32 implementation supports only native threads and is a good model to follow for RTOSs that have a suitable thread package. The solaris implementation uses both a Green threads implementation in src/solaris/java/green_threads and a native threads implementation in src/solaris/java/native_threads.
See Thread Package Notes for more information about thread packages.
Implement a search path mechanism. Most Java implementations have some mechanism for defining a list of locations where class files and other resources can be found at runtime. By convention, desktop implementations use the CLASSPATH environment variable to define a search path that the virtual machine uses for finding class files.
The actual mechanism used for providing this service is implementation-dependent. If the implementation does not provide file system support this feature may not be necessary.
The PJES source code includes three interpreter loop implementations.
Version | Location |
---|---|
C | src/share/java/runtime/executeJava.c |
x86 |
src/share/java/runtime/executeJava_p5.m4
src/share/java/runtime/executeJava_p5.inc |
SPARC | src/solaris/java/runtime/executeJava_sparc.m4.s |
The C and SPARC versions provide a model for implementing an interpreter loop. In addition, some CPU vendors may develop optimized interpreter loops in assembly language for their CPU products. Contact Java Technology Licensee Engineering for more information about optimized interpreter loops.
The easiest way to approach the initial port is to use the C interpreter loop by enabling the CLOOP option in the build makefile. See Build Environment Procedures for more information about build configuration options.
The sample implementations in the PJES use two different thread packages. The solaris implementation has build options for using either native threads or the Green thread package while the win32 implementation is based on native threads.
A port to a native thread package is at a higher level than a port of the Green thread package. Native threads rely on a set of thread primitives defined in the target RTOS's thread library. The porting effort involves modifying the virtual machine source code to use the thread primitives of the RTOS's thread library. The native thread source code for the Solaris implementation is in src/solaris/java/native_threads.
The Green thread package was originally developed for early versions of the JDK. It is made available for RTOSs that don't include a suitable thread package. The Green thread package is a user-level thread package that does not require system/kernel-level thread support.
Porting the Green threads implementation is mainly concerned with implementing the thread primitives with the underlying system interfaces.
Porting Green threads from scratch to another platform may require a fair amount of work depending on the underlying operating system support. If the operating system has a POSIX interface library, then the port should not be too difficult. The Green thread package relies on the UNIX signal model to implement its interrupts. Context switching is another key aspect - the existence of setjmp()/longjmp() capabilities as specified in the ANSI C standard will make the port easier.
The source code to the Green thread package in src/solaris/java/green_threads is heavily commented.
The PJAE can support several different configurations of file system functionality. The list below describes the software layers that contribute to PersonalJava file system support:
The implementation of the file-oriented classes in java.io can be provided by either a native file system, the ROM file system or the java.net.URL class. The choice of implementation is available as a configuration option in the PJES build environment.
Most of the porting work associated with file system support is based on the implementation of the file system-oriented HPI functions. If an RTOS has a POSIX interface library, this effort is straightforward.
Note: Because the file IO-oriented classes in java.io are optional, system-level code should not access these classes directly. Use sun.io.FileIOFactory instead.
The PJES source code includes a simple ROM file system for storing static data files with the PJAE. The ROM file system is an optional component in the sample PJAE implementations that can be used in place of or in addition to a native file system.
There are two important tasks for using the ROM file system:
src/share/sun/jdc/romfiles.c contains the utility routines needed to access files in ROM. This API provides an RTOS with a mechanism for accessing files in the ROM file system.
sun.io.RandomAccessROMFile is a diagnostic class based on java.io.RandomAccessFile that accesses files in ROM by directly calling the utility routines in romfiles.c. Note: This is not production code and is only meant as an example. Among other things, it not reentrant. Two threads trying to access ROM files at the same time could cause errors to occur.
A licensee can choose between several different configurations of file system support. The following table describes the choices.
File IO Support Through java.io | Configuration | Comments |
---|---|---|
No support. | Either the RTOS does not have a native file system, or the licensee has chosen not to expose it through the file IO-oriented java.io classes. |
The following classes in java.io are optional.
See
Configuring the PJES Build Environment
for a description of the configuration options
that control file system functionality.
If a PJAE implementation does not include these optional classes, then it will throw NoClassDefFoundError when an application attempts to access these classes. Software developers who wish to write applications that run on different Java application environments should test for the existence of the optional classes in java.io. This corresponds to the FILEIOPKG=none configuration option. |
Full support. |
+ No ROM file system |
Implement the file system-oriented HPI functions
using the native file system access routines.
This corresponds to the FILEIOPKG=file configuration option. |
+ ROM file system |
Implement the file system-oriented HPI functions
using the ROM file system access routines.
If the RTOS needs access to the ROM file system,
use the functions in romfiles.c.
This corresponds to the FILEIOPKG=rom configuration option. |
|
+ URL-based file access routines |
Provide a null implementation of the file system-oriented HPI functions.
This corresponds to the FILEIOPKG=url configuration option. |
|
+ ROM file system |
First,
implement the file system-oriented HPI functions
using native file system access routines.
Then,
there is a choice of how to integrate the ROM file system.
If the RTOS's native file system
can be extended to handle the ROM file system
as a pseudo file system,
then that is the best approach
because it provides seamless access to both files in the ROM file system
and files in the native system.
Otherwise,
the implementation of the file system-oriented HPI functions
must use access routines
for both the native file system and the ROM file system.
This corresponds to the FILEIOPKG=file configuration option but it also requires additional source code modification to support the integration of the two file systems. |
The following two tables describe the source files in the platform-independent and platform-dependent portions of the virtual machine source code.
Category | Source Files |
---|---|
Class Loader/Linker |
classinitialize.c classloader.c classresolver.c null_preloader.c preloader.c |
Class File Verifier |
check_class.c check_code.c |
Interpreter |
classruntime.c common_exceptions.c exceptions.c executeJava.c inline.c interpreter.c jni.c |
Heap Management/Garbage Collection |
gc.c finalize.c |
Monitor Support |
monitor.c monitor_cache.c |
Thread Support |
threadruntime.c threads.c |
Debug Support |
breakpoints.c debug.c jcov.c |
Category | Source Files |
---|---|
Time Support Functions |
system_md.c |
Memory Allocation Support Functions |
gc_md.c memory_md.c |
IO Support Functions |
dirent.c fd_md.c io_md.c path_md.c system_md.c |
Startup Support Functions |
javai.c |
Thread Support Functions |
threads_md.c |
Monitor Support Functions |
monitor_md.c |
Dynamic Linking Support Functions |
invokeNative_x86.asm linker_md.c |
Termination Support Functions |
system_md.c |
Miscellaneous Support Functions |
condvar.c math_md.c |
Header Files | Description |
---|---|
java/include/io_md.h |
Includes platform-dependent IO header files. |
java/include/typedefs_md.h |
Defines standard types and macros for support longlong, if the compiler is capable of longlong. |
java/include/sysmacros_md.h |
Defines macros that implement basic IO-related HPI functions. |
java/include/path_md.h |
Defines platform-dependent macros for search paths. |
java/include/jmath_md.h |
Includes IEEE-specific header files and macro definitions for DREM and IEEEREM. |
java/include/byteorder_md.h |
Includes platform-specific byte-ordering header files and defines macros for longlong byte ordering. |
java/include/timeval_md.h |
Defines the timeval_t data structure. |
java/include/jni_md.h |
Defines C compiler-specific macros used by JNI to call native method functions and typedefs for jlong, jint, and jbyte. |
java/include/oobj_md.h |
Platform-dependent search paths and oobj defines. |
java/include/fd_md.h |
Header files and function declaractions for file IO-oriented HPI functions. |
java/include/interpreter_md.h |
The REDZONE macros used by sysThreadCheckStack() to define stack red zones are platform-dependent. |
java/include/gc_md.h java/include/jlong_md.h |
Platform-dependent macro defintions for garbage collection. |
java/green_threads/include/threads_md.h java/green_threads/include/monitor_md.h java/green_threads/include/schedule.h java/green_threads/include/context_md.h java/green_threads/include/context.h java/green_threads/include/queue.h java/green_threads/include/internal_md.h java/green_threads/include/itimer_md.h java/green_threads/include/iomgr.h java/green_threads/include/clock.h java/green_threads/include/sparc_md.h |
Platform-dependent header files for the Green threads package. |
java/include/prof_md.h tools/hprof/hprof_md.h |
Platform-dependent header files for the profiler. |
java/include/interrupt.h java/include/interrupt_md.h |
These define macros and function declarations for providing interrupt handling services for the native and Green threads implementations. |
java/include/limits_md.h |
Platform specific limits like MAXPATHNAME or MAXOPENFILES. |
java/include/OSName_md.h |
Name string for host operating system. |
java/include/async_gc.h |
Asynchronous GC function declarations. |
java/include/locale_str.h |
Locale-specific mappings. |
Function | Description |
---|---|
long sysGetMilliTicks(void); |
Return elapsed time in milliseconds. This function is used for profiling. Only the difference between two calls is used, so the elapsed time could be relative to an arbitrary time, such as when the operating system started or the Java virtual machine was created. |
int64_t sysTimeMillis(void); |
Return the current time in milliseconds as defined by java.lang.System.currentTimeMillis. |
Function | Description |
---|---|
void * sysMalloc( size_t size); |
Memory allocation. |
void * sysRealloc( void *ptr, size_t size); |
Change the size of a block of memory to reflect the new size. |
void sysFree( void *ptr); |
Free memory allocated via sysMalloc(). |
void * sysCalloc( size_t nelem, size_t elsize); |
Allocate memory initialized to 0's. |
void * sysMapMem( size_t requestedsize, size_t *mappedsize); |
Map a range of virtual memory supplying the request size. The mapped size is returned in the second argument. Backing store is not reserved. |
void * sysUnmapMem( void *requestedaddress, size_t requestedsize, size_t *unmappedsize); |
Unmap a range of virtual memory supplying the address and size to unmap. The size of the memory unmapped is returned in the third argument. |
void * sysCommitMem( void *requestedaddress, size_t requestedsize, size_t *unmappedsize); |
Commit backing store to a range of virtual memory. The range of memory supplied should already be mapped. |
void * sysUncommitMem( void *requestedaddress, size_t requestedsize, size_t *unmappedsize); |
Uncommit backing store to a range of virtual memory. The range of memory supplied should already be mapped. |
void * sysAllocBlock( size_t block, void** allocHead); |
Allocate size bytes on a specified alignment boundary. Note: Only required if implementing paged heaps (PAGED_HEAPS). |
void sysFreeBlock( void* allocHead); |
Free memory allocated by sysAllocBlock(). Note: Only required if implementing paged heaps (PAGED_HEAPS). |
Function | Description |
---|---|
int sysAccess( const char *pFile, int perm); |
Determine the accessibility of a file. |
int sysStat( const char *path, struct stat *sbuf); |
Retrieve file status. |
int sysRename( const char *srcName, const char *dstName); |
Rename a file. |
int sysUnlink( const char *file); |
Unlink a file or directory. |
int sysMkdir( const char *path, int mode); |
Create a directory with the specified mode/permissions. |
int sysRmdir( const char *path); |
Remove a directory. |
DIR * sysOpenDir( const char *path); |
Open a directory as specified by path. |
int sysCloseDir( DIR *dp); |
Close the indicated directory. |
struct dirent * sysReadDir( DIR *dp); |
Returns a pointer to a structure representing the directory entry at the current position in the directory stream to which dirp refers, and positions the directory stream at the next entry. It returns NULL upon reaching the end of the directory stream, or upon detecting an invalid location in the directory. |
int sysIsAbsolute( const char *path); |
Returns true if the path supplied is absolute (as opposed to relative) as defined by java.io.File.isAbsolute. |
int sysCanonicalPath( char *path, char *result, int result_len); |
Convert a path to its canonical form placing the result in result. |
int sysOpenFD( Classjava_io_FileDescriptor *fd, const char *name, int openMode, int filePerm); |
Open a file. |
int sysCloseFD( Classjava_io_FileDescriptor *fd); |
Close a file. |
long sysSeekFD( Classjava_io_FileDescriptor *fd, long offset, int whence); |
Move the read/write pointer for the specified file. |
size_t sysReadFD( Classjava_io_FileDescriptor *fd, void *buf, unsigned int nBytes); |
Read a file. |
size_t sysWriteFD( Classjava_io_FileDescriptor *fd, const void *buf, unsigned int nBytes); |
Write a file. |
size_t sysSyncFD( Classjava_io_FileDescriptor *fd); |
Synchronize a file's in-memory state with that on the physical medium. |
int sysAvailableFD( Classjava_io_FileDescriptor *fd, long *bytes); |
Return true if the file is available for reading (without blocking), setting the size of the file in the second argument. This is used to implement java.io.FileInputStream.available. |
void sysInitFD( Classjava_io_FileDescriptor *fdobj, int fd); |
Initialize FileDescriptor object to corresponding to integer file descriptor, based on a platform-specific mapping. |
Function | Description |
---|---|
void sysGetDefaultJavaVMInitArgs( void *args_); |
Returns the default virtual machine initialization parameters. |
int sysInitializeJavaVM( void *ee_, void *args_); |
Platform specific initialization. |
int sysFinalizeJavaVM( void *ee_); |
Required by JNI Invocation interface. Called before destroying virtual machine. Platform specific cleanup. |
void sysAttachThreadLock(void); |
Acquires lock to be held while a native thread is "attached" to the virtual machine - see the JNI interface function AttachCurrentThread(). Note this lock is held only during the process of attaching the native thread. Once attached, it is released by sysAttachThreadUnlock(). |
void sysAttachThreadUnlock(void); |
Releases a lock held while a native thread is "attached" to the virtual machine - see the JNI interface function AttachCurrentThread(). Note this lock is held only during the process of attaching the native thread. Once attached, it is released. |
Function | Description |
---|---|
int sysThreadBootstrap( sys_thread_t **ptid, vois *cookie); |
Turn current code context into the first thread. |
void sysThreadInitializeSystemThreads(void); |
Initialize system threads such as the clock, idle, and gc threads. |
int sysThreadCreate( long ss, uint_t flags, void (*start)(void *), sys_thread_t **ptid, void *cookie); |
Create a new thread. Success is indicated by a SYS_OK return value. Failure is indicated by SYS_NOMEM (out of memory) or SYS_NORESOURCE (shortage of non-memory resource). |
void sysThreadExit(void); |
Terminate current thread. |
sys_thread_t * sysThreadSelf(void); |
Obtain caller's thread ID. |
void sysThreadYield(void); |
Non-preemptive CPU yield. |
int sysThreadSuspend( sys_thread_t *tid); |
Suspend execution of specified thread. |
int sysThreadResume( sys_thread_t *tid); |
Resume execution of specified thread. |
int sysThreadSetPriority( sys_thread_t *tid, int priority); |
Set the scheduling priority of a specified thread. |
int sysThreadGetPriority( sys_thread_t *tid, int priority); |
Get the scheduling priority of a specified thread. |
void * sysThreadStackPointer( sys_thread_t *tid); |
Access to the thread stack pointer of an arbitrary thread. |
stackp_t sysThreadStackBase( sys_thread_t *tid); |
Returns the stack base (highest valid stack address) for the specified thread. Initially this would correspond to the bottom most (oldest) stack frame, but can be overridden by sysThreadSetStackBase. |
void sysThreadSetStackBase( sys_thread_t *tid, stackp_t sp); |
Specifies a new value to be returned by future calls to sysThreadStackBase. |
int sysThreadSingle(void); |
Run current thread in exclusive execution mode and disable context switch. Returns SYS_OK for success and SYS_ERR for failure. |
void sysThreadMulti(void); |
Return current thread to concurrent scheduling mode; enable context switch. |
int sysThreadEnumerateOver( int (*)(sys_thread_t *, void *), void *arg); |
Enumerate over all threads, calling a function for each one. |
void sysThreadInit( sys_thread_t *tid, stackp_t stack); |
Called at the start of a new thread from within the context of the newly running thread. |
void * sysThreadGetBackPtr( sys_thread_t *t); |
Returns a Java level thread identifier for a native thread. |
int sysThreadCheckStack(void); |
Thread C stack overflow check. |
int sysInterruptsPending(void); |
Return 1 if interrupts pending, 0 otherwise. |
void sysThreadPostException( sys_thread_t *tid, void *exc); |
The mechanics of actually signaling an exception. |
void sysThreadDumpInfo( sys_thread_t *tid); |
Dump system-specific stuff about a thread, e.g. its native thread ID. |
void sysThreadInterrupt( sys_thread_t *tid); |
Support for Java-level interrupts. |
int sysThreadIsInterrupted( sys_thread_t *tid, int ClearInterrupted); |
Atomically read and clear the boolean flag depending on the value of ClearInterrupted that's passed in. |
int sysThreadAlloc( sys_thread_t **ptid, stackp_t stack_base, void *cookie); |
Allocate and initialize the thread resources for an arbitrary thread. |
int sysThreadFree( sys_thread_t *tid); |
Free a system thread block. Remove from the thread queue. Delete the associated ExecEnv. Notify everyone in the monitor queue. |
Function | Description |
---|---|
size_t sysMonitorSizeof(void); |
Return the size of the platform-dependent portion of monitors. |
int sysMonitorInit( sys_mon_t *mid); |
Perform any system-dependent initialization of monitors. |
int sysMonitorDestroy( sys_mon_t *mid); |
Free any system-dependent resources held by monitors. |
int sysMonitorEnter( sys_mon_t *mid); |
Enter a critical section. |
bool_t sysMonitorEntered( sys_mon_t *mid); |
Return true if the calling thread is the current owner of the monitor. |
int sysMonitorExit( sys_mon_t *mid); |
Exit a critical section. |
int sysMonitorNotify( sys_mon_t *mid); |
Unblock the thread at the head of the wait queue for this monitor. |
int sysMonitorNotifyAll( sys_mon_t *mid); |
Unblock all threads waiting on this monitor. |
int sysMonitorWait( sys_thread_t *mid, sys_mon_t *millis, int64_t clear); |
Block and wait for subsequent sysMonitorNotify() or sysMonitorNotifyAll() call. |
void sysMonitorDumpInfo( sys_mon_t *mid); |
Dump monitor state including blocked threads. |
sysCacheLockInit |
This macro initializes the monitor cache lock. |
sysCacheLock |
This macro sets the monitor cache lock. |
sysCacheLocked |
This macro checks the state of the monitor cache lock. |
sysCacheUnlock |
This macro removes the monitor cache lock. |
Function | Description |
---|---|
char * sysInitializeLinker(void); |
Called by java.lang.Runtime.initializeLinkerInternal to initialize dynamic linking. Returns a search path for libraries. Paths are separated by the character defined in the path.separator system property. The empty string ("") is a valid return value and is the value returned by the reference ports. This function should perform initializations necessary to perform future dynamic link functions. |
int sysAddDLSegment( char *function); |
Load the specified shared library.
The path name is a complete path,
such as one passed to java.lang.Runtime.load
or returned by sysBuildLibName.
Return values:
|
long sysDynamicLink( char *symbol_name); |
Look up a symbol dynamically and return its address. A return value of 0L indicates failure. |
void sysBuildLibName( char *buf, int buflen, char *prefix, char *name); |
Create a string for the dynamic library open call (sysAddDLSegment) by adding the appropriate prefix and extensions to the filename and path. For example, Win32 appends ".dll", while Solaris prepends "lib" and appends ".so". |
int sysBuildFunName( char *buf, int buflen, struct methodblock *mb, int encodingIndex); |
Construct a function name appropriate for lookup in a shared object. Returns 1 for success and 0 for failure. This function is first called with an encodingIndex of 0. It is retried with higher values until sysBuildFunName() returns failure or sysDynamicLink() on the constructed name returns successfully. |
long * sysInvokeNative( JNIEnv_ *env, void *address, long *optop, char *sig, int argSize, void *staticRef); |
Translates the Java calling convention into the C convention used in JNI-compliant native methods. |
Function | Description |
---|---|
void sysExit( int status); |
Exit the virtual machine. |
int sysAtexit( void (*func)(void)); |
Install a function to run when the virtual machine exits. sysAtExit() can be called multiple times. |
void sysAbort(void); |
Exit and dump debugging information. |
Function | Description |
---|---|
sysAssert(expression) |
This macro provides a system-dependent assertion facility. |
sysCheckException(exception) |
This macro checks whether an exception occurred. It provides an opportunity to use the already-required exception check as a trap for other system-specific conditions. |
sysStricmp( const char *s1, const char *s2); |
This macro performs case-insensitive string comparison. |
cpe_t ** sysGetClassPath(void); |
Returns a copy of the CLASSPATH variable. |