Kueea System is composed of Kueea Networks. It is an abstract thing that does not physically exist. There exists only one Kueea System; do not use ‘the’ in English.
A Kueea Network is a computer network, in which every node is a Kueea Node - one that supports a set of network services required by implementation of corresponding Kueea Modules, which include the system’s kernel and the general-purpose remote procedure call (RPC) network service.
The term peer or node refer to an entity that is connected to other such entities with some kind of network technology. Ethernet and Internet are examples of such technology. Generally, in Kueea System documents, nodes refer to a single thing, whereas peers are used in the context of a pair of nodes.
Modules and M-Builds
A Kueea Module is a technical document written in Kueea Module Definition Language (abbreviated as KMDL). It defines symbols – functions and data objects of various kind. Once a symbol is declared as final, its definition never changes.
Symbols are grouped under module levels. Implementations of a module at level n contain all symbols defined by levels [0,n] of that module.
An M-Build is an implementation of a module.
An M-Build image contains one or more M-Builds. They are program images interpreted and executed by system software. The system is not dependent on any particular image format, although there always exists a preferred format for these images.
Each module’s goal is to standarize some specific system functionality. Modules ought to follow the KISS (Keep It Simple, Stupid) rule. They are to be narrow in scope so that their developement ends one day, the module becomes finalized and no more symbols are ever added to it. If it does not need revision, it means it’s good and can be safely used.
Having too much functionality in a module makes it more unstable. Stability is the most important trait every module author ought to pursue. Modules that are constantly revised are broken by design. Such modules ought to be scrapped, made obsolete, and then redesigned as new modules (with a new identifier).
The scale of a module should ideally be small enough for one person to not become mentally exhausted (burned out) while implementing it alone. There should be many implementations available that a user can choose from.
One should differentiate between a module and a software project. The module ought to be a part of the project, not the project itself. For example, if a Python interpreter were to be a module, then Python 2 and Python 3 should be different modules developed as part of the same project. They are different because the most crucial part—the parser and interpreter—are different for version 2 and for version 3. The other reason is that Python 2 is phased out in favour of 3. Keeping both 2 and 3 in the same module is counter-productive. Remember that symbols—once defined—cannot be removed from a module.
Some modules are published and standarized for the general public. Other modules may be known only to select few or be created as part of developement or a user session and have the lifetime of only few hours.
Programs and memory types
Kueea System programs are executed by an abstract computer. The processors of the computer do not matter; they are undefined. What matters, however, is the memory that the processors access.
The abstract computer is implemented as a Kueea Network. Memory of all its nodes is the memory of this abstract computer.
There are three distinct memory spaces within Kueea System.
Network (system) memory
Network memory is made of octets, each having a 192-bit address. The higher 128 bits of the address reference a node in a Kueea Network. The lower 64 bits reference an octet allocated on that node. Reference to this memory is called a handle.
In other words, each node can allocate at most 264 octets. Also note that one physical machine may function as more than one node, because the whole network is virtual along with all of its nodes.
Upon object allocation, an address range is associated with an object. Throughout the lifetime of the object, its address range does not change. The address range may be reused only after said object is deallocated.
At one point, the authors considered hardware that could reference network memory directly, but that idea was abandonded. The problem lies in that if the memory was physically at another node, fetch operation would complete after the data arrives over the network link. The CPU would not be able to execute the next instruction or be interrupted. This would introduce very long delays, which is unacceptable. Long-distance network links also introduce a bunch of other problems. The 192-bit network memory is thus entirely implemented in software.
System tasks access memory through their virtual memory. The memory space is made of bytes, which must be at least 8-bit long.
Upon calling a function and when returning from one, bytes of objects passed as parameters must have all bits above the lowest 8 cleared. The bits must also be kept cleared whenever an object is, or may be, accessible to more than one task. In other words, the bits are assumed to be cleared when loading.
The bytes are referenced by 64-bit virtual addresses. These addresses are unique to a given task. Virtual addresses are defined to map into network-memory addresses.
Current (as of 2019) CPUs reference memory with 64-bit values, of which not even all of the 64 bits are used for the address. The authors believe 264 octets is enough for any task, although some once thought that 16 MiB of RAM is enough.
Actual object data resides in a machine's physical memory, i.e. the memory space at which physical (real) devices are mapped onto. Reference to this memory is called a physical address.
In practice, virtual addresses are translated by a CPU into these directly. If the referenced memory is not part of the physical memory of the machine, the reference is intercepted and dereferenced by system software.
The point of this is to write programs without thinking about or caring whether an object is physically at the current machine or at another. Physical location of an object is managed by system software. This includes transferring the data from one node to another. Programs may instruct the system to move an object between nodes, in which case its network address changes and handles need to be updated. Not every object can be moved in this way.
Kueea System is an object-oriented system.
An object has the following traits:
- location of its data (handle and physical address),
- length of the data (in octets),
- type of the object (class),
- level of data confidentiality and other attributes.
The confidentiality level determines what kind of memory to allocate, whether to encrypt it and if the memory needs to be cleared or not. Copying data from a higher level to a lower level is a security error.
The lifetime of an object is as follows:
- A task requests the kernel to allocate memory for an object.
- The task gains access to the object’s memory by the kernel mapping it onto the task’s virtual memory space.
- The task invokes the object’s preconstructor; i.e. initializes the object to a safe state. (Precostructors never fail.)
- The task invokes the object’s constructor. If the constructor fails, skip to step 7.
- The object is now created.
- Tasks utilze the object.
- Tasks lose access to the object by the kernel unmapping it from the task’s virtual memory space.
- When there are no more references to the object, the kernel invokes the object’s destructor in a new task.
- The kernel deallocates the object’s memory.
This poses a question: What should the kernel do when a destructor fails? The preferred answer of the authors is to log the event and continue.
Classes and interfaces
Every object is an instance of some class. Classes are made of data and function members. Function members are often called methods of the class.
Data members of a class are instances of a class or octets, (The system defines only one fundamental class – an octet). Each data member is located at a predefined offset into the object; i.e. a class defines precisely how its data members are laid out in memory.
Similar to modules, classes also have levels. Higher levels add new members to a class. New data members may only extend the object.
Classes have memory address alignment requirements. Data members should be arranged from longer alignment to shorter alignment. Inserting padding between subsequent members is strongly discouraged.
A class may be declared as an interface. If the interface defines data members, a class that implements it must contain these members somewhere within its data structure and is expected to implement all of the required function members that the interface defines.
Information about classes and their interfaces is stored within special read-only objects called class descriptors.
Tasks may access this descriptor, which allows them to execute interface functions without any prior knowledge about a given object. Programs are written to either manipulate an object based on its type or based on the object’s implemented set of interfaces.
In other words, run-time type information about objects is a part of the system’s ABI in order to provide system-wide virtual function functionality to every task in the system, regardless of the language a program has been written in.
Tasks are created as part of answering a query to the system. They begin at any function and finish when that function returns. A task may also fail, which is distinct from a successful return.
When a new task is created by the system, the caller task becomes the task owner of the callee. Ownership relations form a hierarchical tree structure. When a task is taken out of the tree, it becomes a detached task.
Task ownership may be transferred to another task, as long as the target task is located deeper (in the tree) than the current owner and the caller. Detached tasks are an exception - any task may become their owner.
The first task a node executes is called its boot task, which is placed at the root of the task tree.
Tasks may return a value, which other tasks can later obtain. Values returned by detached tasks are forgotten immediately.
Tasks are deleted when in detached state and have returned or failed.
Tasks access objects though their virtual memory, which is composed of three memory spaces called access contexts:
- task context, accessible only to a particular task;
- module context, accessible to all tasks of a given module; and
- kernel context, accessible only when the task is in kernel mode.
A task in kernel mode has priviledged access to the machine. What exactly does this mean depends on a particular CPU architecture.
Objects in module and kernel contexts are independent of tasks.
Function symbols are categorized by which context they need access to:
- task functions access their arguments only;
- module functions access objects in their module’s context; and
- kernel functions access objects in the kernel context.
Tasks and objects
The system assumes objects are big and complex data structures. Copies of objects are never created by the kernel.
All memory is shareable between tasks (including the stack.) The kernel’s main job is controlling access to object memory.
The core functionality of Kueea System is tasks’ ability to gain direct access to objects created by other tasks. This operation also gives access to any referenced subobjects.
A module is technically a special type of class. There is at most one instance of these classes per node. This object is referred to as the module’s state.
The module state is destructed at first (no instance). When destructed, the module's constructor may be called. If successful, the module becomes constructed. Once constructed, module functions of the module can be called.
The state is shared between all implementations of a module on a node.