Article contributed by GilbertHerschberger (6 December 1999).
When a module plugs into a kernel, the kernel must provide a mechanism to load and unload modules. The kernel invokes only machine code methods, not bytecode. A bytecode interpreter is beyond the scope of a kernel.
A kernel module itself is machine code. It is native code running on a specific processor. Through classic porting techniques, a machine code modules can be ported to more than one processor.
A module uses the kernel to load other modules. The kernel interface is available to modules. If a module is-a virtual machine, it can interpret bytecode. If a module obtains a handle for a virtual machine, it can use a virtual machine to interpret bytecode.
The kernel creates a linked list module holders. When a kernel is created, the linked list is empty. Modules are held by module holders, after they are loaded and initialized. Handles are managed by the module holder, not by a kernel nor by a module.
static KernelModuleHolder *Kernel::top = NULL; class KernelModuleHolder { public: : virtual MODULEHANDLE openHandle(); virtual bool isOpen( MODULEHANDLE v ); virtual void closeHandle( MODULEHANDLE v ); virtual KernelModuleHolder *getNext(); virtual KernelModule *getModule(); : private: KernelModuleHolder *next; KernelModule *module; int count; int *handles; }
When an application needs to load an external module, it passes the name of the external module to the kernel and gets a MODULEHANDLE in return:
typedef int MODULEHANDLE; MODULEHANDLE mh = Kernel.loadModule( "module-1" );
The module handle is an index into module list. Once a module has been closed, its module handle is obsolete.
When an application needs to unload an external module, it passes a module handle to the kernel. The kernel shuts down an external module, releasing all of its resources.
Kernel.unloadModule( MODULEHANDLE v );
Kernel modules do not run in their own thread. They are not part of an independent process. A kernel module might have more than one module handle. The kernel does not need to remove a module until all handles to the module have been closed.
When a module is loaded, its init() method is invoked by the kernel. Before unloading a module, its exit() method is invoked by the kernel. When a module is loaded, any process can use it through its module handle.
Method[] Kernel.list( MODULEHANDLE v );
The kernel is asked for a list of all static methods available in a module. In turn, the kernel asks a module for a list of all its methods.
void Module::list( char *&list );
int Kernel.getMethodID( MODULEHANDLE v, String v );
The kernel is asked by an application for the method ID for a specific method in a module. In turn, the kernel asks a module for its method ID.
int Module::getMethodID( const char *v );
As a general rules, kernel modules have a priority of zero. Their timeslice() method returns immediately because a kernel module does not run itside its own "thread". A general purpose module runs inside the "thread" of a virtual machine.
Unlike general purpose kernel modules, a virtual machine has its own priority and timeslice. When a virtual machine is loaded as a module, it must be plugged into the timeslice mechanism of a kernel.
Therefore, the explicit difference between a "module" and "thread" from the kernel's viewpoint is the module priority. A "thread" with a no-op priority (like zero) is a "module". When the following method is called with priority equal to zero, it returns control to the kernel immediately.
void ModuleHolder::timeslice() { int iMax = module.getPriority(); for ( int i = 0; i < iMax; i++ ) { module.step(); } }