Kernel Dynamics System
The main idea behind the Kernel Dynamics System is to allow easy run-time linking of some modules and server into the kernel. Most of those linkings are made at boot-time, but some of them might change during the session (by example, if you change the Operating mode or if you use hot-plug peripherals) A module is a pack of objects that will "upgrade" the kernell, i.e. that will either add some new services or improve some existing ones.
The K.D.S. is intended to give three types of services:
|goal||The naming system allows to store some name-value combinations. A name can be any "regular" character-string (restricted to letters, digits and a few special symbols as _, -, @, &, ...). The value can be any unsigned int. A binding between a name and a value will be called a key and the name and value will be called key-name and key-content or key-value respectively.|
|extension||The kernell has a main naming system, that is, a wide set of keys that is globally accessible to the whole system. This keyset is structured hierarchically by the mean of subsets. The notation convention is to separate each new subset by a dot, as if it was fields in a C structure. So "user.application.emacs.foreground" means "the foreground key in the emacs subset, which stands in the application subset of user. To allow subsets, each key gets a few bits of flags to determine whether it is a single key or a set of keys, etc.|
|KDS_SET||creates an empty subset of keys rather than a single key|
|KDS_CONSTANT||the binding cannot be changed|
|KDS_SERIAL||do not use the given value (if any) but rather assign a new unique-id as value|
Most of them are self-explanatory, so i won't give more details to them. Note that all of them need a "namer" object as first parameter. This can be the global-namer (if you invoke the system-call) or just any custom-namer.
Hope this doesn't sound too much like the sucking MS registery base.
|goal||The Network Object System is designed to provide a way to group module that often communicate together. Each "network" provide a single service to its clients. This service may be implemented by one or more servers.|
|extensions||Each service has a unique name that identifies it. Those names are hold by the naming system in the keyset "services.*". They may take advantage of the hierachical api, so you can create a subset of services "services.fs.*" that will hold every service related to the file system (for instance).|
|operations||The main operations on a network is to add/remove entities (i.e. servers and clients). On another hand, you also have primitives to send messages from clients to servers and some other ones to reply messages from servers to clients or broadcast messages.|
For those who knows it, it will probably sounds like the V2OS servers system, except i don't think there's anything about interfaces in V2OS
Each services (and thus each network) may provide one or more set of function (called interfaces) to its clients. An interface is defined as an interface name and a set of methods. It also holds a list of servers that implement this interface. These informations are stored in a namespace as the kernel main namespace, but local to the service. I suggest you give a look to the example. It shows the "files" service with its three possible interfaces (generic for open/close and delete methods, array for traditional access and set for db-like access).
In addition to the interfaces definitions (i.e. ordered list of methods names), each server that wants to provide the service to an interface is expected to fills an interface table with pointers to the functions that will implement the corresponding methods. The order of function in that table must be the same that the one declared in the interface definition.
|id newInterface(service,name)||creates a new (empty) interface for the given service. If an existing interface has the same name, a NULL id is returned.|
|id addInterface(service,name)||same as newInterface, but returns the ID of the existing interface if any or create a new one. (always succeed).|
|int addMethod(service,interface_id,name)||adds a method for an interface. If the given name is already defined, the offset of the previously existing one is returned.|
|int newMethod(service,interface_id,name)||same as addMethod, but will fail (i.e. returns -1) if the method already exists|
There are two ways using interfaces. The first one is to use them to send messages. That is, you'll call a generic function send(service,message_code,message_params...), where the message_code has the format "interface:method". The send function will then find back the corresponding function (after it has choosen the target-server according to the deliver-policy of the network) and call it with the given message_params.
Another way using interface is to query the "interface:method" that the client plans to use and store the results in a custom structure. Once this structure is filled, you just call those function with the right parameters. The query(service,message_code,...) call will return a NULL pointer if the requested interface doesn't exist or if it doesn't hold such method.
Note that the client that gets a method from a server through the query call will be registered as a user of that server. This means that, if the server has to be removed while it still has registered user, they will be notified of this through their update(message_code) function. The common response of this function is to try to query the method again (with the remaining servers), and to stop the activity of the client when that query fails. See the details about versions of the same service for more informations.
|void *send(service,code,message...)||sends out some message to a server of the given service. The message code has the form "interface:method". The corresponding function is found and called with the message as arguments.|
|implementation *query(service,code,registernfo *update)||returns the interface corresponding to a given "code", and register informations for the use of the given interface.|
|function getmethod(service,code,implementation)||retrieves the address of a method from a message code "interface:method" and an implementation (probably got through "query").|
A server is a bunch of function that will provide the service. That is, if you're designing the "services.video", you could write one server for each hardware (and put them into modules). Each of them may provide one or more interfaces for the different group of functions it will provide, so it'll looks like
|S3 Trio 64||mode select-bitmaps|
|S3 ViRGE||mode select-bitmaps-3D|
|Riva TNT||mode select-bitmaps-3D-MPEG|
Note that the implementation of the interface "bitmaps" will probably be different for each server, while the design of that interface will be the same for each one (clear(), bitblt(), copyArea(), clearArea(), drawLine(), etc.)
|char *name||The server-name is a string that is supposed to identify it in reports, etc. The server-name has to be unique on the network, any attempt to violate this rule will result in the fail of the second server's creation. If the server has been installed by loading a module, then the name of the server will be the name of the module.|
|pid owner||Tells which program owns the server. A zero value means that the server was installed by a kernel module, not by a "regular" program.|
|list *interfaces||This list holds one component for each interface implementation. The main part of an implementation is a table of pointer to functions that mathces with the interface definition. See the details for more infos about the structure of those implementation.|
|list *clients||This list holds information about the server's clients that have queryed some function. This list is sweeped at server's disconnection to warn those clients. It's also a common way to store dependencies between modules while a module may be server on a network and client on another one.|
|void *extra||In some special cases, this field may contain a structure of deliver-specific informations (like a mountpoint for a server of the filesystem).|
|server *newServer(service,name,[extra])||This creates a new server for the given service, and tells its name and eventually the extra parameter (NULL if not necessary). The new server got the pid of the caller as owner field, and has both interfaces and clients lists empty|
|implementation *provide(service,server,interface)||This registers the given server as a provider for a given interface. An empty structure describing the interface is allocated and inserted into the server's interfaces list. In order to avoid use of unready implementations, a flag "ready" is tested when choosing the target-server. After calling "provide", the empty implementation has this flag cleared.|
|void activate(service,interface,implementation)||This sets the "ready" flag of the given implementation. It also calls one of the network-function to inform clients that a new implementation is ready. In some network, this will make all old clients to update their links so that they rather use the newest version.|