Are you ready for the Clicker generation ?

Kernel Dynamics System

Disclaimer

This document has not yet been reviewed and may be a bit outdated vs the kernel sources. Anyway, it should be useful to understand what concepts lies under KDS and how we plan to use it in Clicker Kernel Architecture. It should lead to a CAKE as soon as possible.

The Main Purpose

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:

  • naming: through the naming system, it allows to register some name/value bindings and to assign serial numbers to names (substitutes strings a unique id). Moreover, it's possible to define sub-namespaces and creating hierarchic names.
  • services: each service (i.e. filesystem, video access, memory management, etc.) is made of a network where servers (modules that provides that service) and clients (modules that requires that service) are interconnected. Each network is given a unique service-name, which allows potential clients to find it through the naming system (services.*).
  • dlods: the DLOD support allows dynamic linking and exchange of objects or datas. Many of the kernell-to-apps interface (the kAPI) and also some of the kernell-to-kernell interface (the SPI) widely uses that feature.

  • The Naming System

    Overview

    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.
    uses
  • make unique id's. This is the main purpose, you can assign each new name in the namespace a integer that won't be used by any other names.
  • holding constants. It might be useful to use that naming system to store some "runtime constants", this is some values linked to the machine itself (or its environnement) that can't be easily known at compile-time. So in the namespace "sound" you could link "sound.dsp=0x220", "sound.irq=5" and "sound.dma=1", so any part may know the
  • using short symbolic names. It's quite useful to be able to use a symbolic name, but they're often too long to store or compare. with the naming system, it's possible to use a regular integer as a symbolic name. In fact, you'll just have to "register" the symbolic name with the UID feature enabled, and then use the returned UID. You can also get back the full name if you need it for printing (from the UID), even if this operation can't be guaranteed to be as fast as the search by name.
  • Flags

    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
    Note: the flags KDS_SET and KDS_SERIAL can't be set together, while the value of a KDS_SET is the namer

    Operations

    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.

  • int register(namer *me,char *name, int flags, int value)
    Note: if the flag KDS_SERIAL is set, the content of value is not used.
  • bool unregister(namer *me,char *name)
    Note: by unregistering a keyset, you will automatically unregister (i.e. destroy) all the keys or subsets it holds.
  • int seek(namer *me,char *name)
    Note: the key-value of a keyset is the namer object of this set, so you can either search the full path each time ex: "services.filesystem.interfaces.readonly"),or search first the subset you need ("services.filesystem.interfaces") and then look in it for your key ("readonly").
  • int seekid(namer *me,int id)
    Note: The result of a id-search will only look in the UNIQUE-ID's reference, so a match with a key where KDS_SERIAL is off won't be returned. A nul-value as return means that no match has been found.

  • Hope this doesn't sound too much like the sucking MS registery base.


    Services and Network Objects System

    Overview

    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

    Interfaces

    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

    Using interfaces (the Clients side)

    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").

    Servers

    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

    serverinterfaces
    S3 Trio 64mode select-bitmaps
    S3 ViRGEmode select-bitmaps-3D
    Riva TNTmode 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.)

    Server Components

    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).

    Servers-related functions

    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.

    Last modified: Mon Aug 28 22:18:32 /etc/localtime 2000