OS "Sivelkiriya": an example of building a program

Hello, Habr.

This post continues the cycle of publications about the Sivelkiriya OS project. The first article of the cycle gave a general description of the concept, the second explained why it was needed and in what form the product could see the light, the third described architectural solutions, and the fourth answered the question of how to coordinate the actions of OS developers and Software in this model. This article will show an example of splitting a simple program into modules in order to fit it into the realities of the new OS.

Consider a classic example: a program that allows you to open a file containing a bitmap image and view it on the screen. The image can be scaled, and if its size at the current scale exceeds the screen size, scroll.

In modern operating systems, this problem is often solved by an application with a closed architecture, that is, one that either itself contains all the required code, or uses third-party components purely on its own initiative. The key point is that you cannot use part of the application - you can either start it as is or use another solution.

In order to understand how this problem is solved within the framework of the Sivelkiriya OS, you will first have to look “under the hood” of this program and understand what it does and what concepts it operates with. The following are the entities that arise during the operation of the program.

  1. The location of the image.In the classic case, it is described by the path to the file on disk. With a little broader consideration, addresses in the local network or on the Internet also fall into this category. This, however, does not exhaust all the possibilities: the image can be located in the RAM, at the output of some program (for example, a photo processor or a graphics renderer), on a web page, in a chat or email message, inside an archive or office document. Despite the fact that technically all of these options are different from each other and require different processing, from the point of view of the user they all serve one purpose: they indicate where the image that he wants to view is located. Creating an interface that allows you to choose among so many options is a non-trivial task, but conceptually there are no obstacles to this approach.Moreover: there is no reason why the user should not be given the opportunity to view the image located in any of the above locations.
  2. , . , , , .
  3. . , , jpeg gif. : jpeg .gif, gif . , , , , web-.
  4. , ( ) .
  5. . , tiff, . — , gif — .
  6. .
  7. : , , , .
  8. .
  9. ( ) , .
  10. ( ), .

Within the Sivelkiriya OS, each of the entities listed above is described by a certain data interface. Such a representation allows their use in many contexts outside the original model (which will be discussed later).

Each interface is implemented by some object, which is created by some module. The code of the object's methods is executed within the framework of the module that generated it. At the same time, it is incorrect to talk about launching modules as separate applications, since they have neither their own threads, nor memory outside the objects created by them. If you need to store a state that goes beyond working with a single image, it is stored in a separate object, access to which is carried out according to general rules - through the OS object interface.

The structure of the modules involved in the work of this program may look like this:

  1. The first module defines the behavior of an object that implements the “Object location” interface (an image is a special case of an object). In particular, it determines how to access bytes from a given location. However, he can use modules that provide direct access to the disk or network support, depending on the physical location of the object.
  2. The second module provides direct access to image bytes. In addition, it provides hints about the possible type of content based on the storage method (file extension, web server headers, etc.).
  3. The third module, using hints about the type of content and access to its byte representation, determines the actual type of content (image file format).
  4. , ( , , ) ( ) . , : , . — : jpeg .
  5. , , , , . — — , , .
  6. , . , . — , , , , , , , .

Of course, in addition to the described modules, the program can take part in modules that provide additional features: logging, rendering of window components, audio feedback, and so on.

It is easy to see that such a structure makes reusing implemented functionality as simple as possible. So, installing a new codec in the system means that this type of image will be automatically supported in all contexts and in all programs. Different modules can be written by different programmers and in different languages, however, in the framework of this interaction model, these differences are not significant. One and the same codec can be used both for loading images onto a screen in this interface, and for rendering them in a messaging program, browser, directory contents viewer, and so on.

Support for new use cases is carried out elementarily. For example, adding a few additional interfaces allows you to support actions such as moving to the next and previous images (regardless of their location and how to access them) or applying filters. The introduction of a thin client is also not a problem: since the passage of data and calls through the borders of the module is controlled by the operating system, it is possible to transfer costly operations (for example, decoding the contents of a file and scaling an image) to another machine.

Since the prototypes of all the modules described above are known to the operating system, it knows about their needs from a system point of view and can act accordingly. For example, the image scaling operation can be performed in a separate thread so that work with large images does not block the interface on low-end computers. Moreover, since the modules do not make assumptions about the threads in which they are executed, additional optimizations are possible: for example, the user interface thread can be separated from the thread responsible for computing (in this case, scaling), only if the latter I didn’t have time to finish the work within a predetermined period of time, and with a quick re-scale, drawing the image at a new scale can be started in an additional stream even beforeas a thread engaged in rendering, which no longer needs to be completed, it will manage to process the signal to completion. Since the operating system has information about all running threads, about the loading and actual capabilities of the computer's processors, the optimization data can be more effective than those that the author of a separate application can do based on assumptions about its execution environment.

The question of how the modules that jointly provide a solution to an applied problem are linked together can be solved in several ways. For example, it is obvious that the choice of a module that reads bytes of a file from disk is determined by the file system of this section, and in all cases of access to the same section, the same module will be used. The module responsible for determining the image format is likely to be installed at the system level and used in all contexts. An exception may be cases when a module fails: in this case, the operating system can search for another installed module that matches this prototype, and if it exists, try to use it with the same input data. In this way,the error can be processed without user intervention, and data about it is collected and, if allowed by the security policies of this machine, passed to the developers of the problem module along with the necessary related information. If a module often has problems, the operating system may decide to exclude it from the search chain or lower its priority in it.

In other cases, the choice of module can be user-defined and stored in the configuration. So, different image scaling modules can provide a different rendering style (anti-aliasing parameters when zooming out or blurring the borders of pixels when it increases). Depending on the context, the user may need a different approach (sharp pixel borders for precise positioning or blurry for visual comfort).

Methods for starting modules may also vary. For example, the module responsible for rendering the image viewer window can be called up by the module responsible for rendering the desktop (by clicking on the graphic file on the desktop), or by the module that displays the program launch menu. After starting, the window module loads those modules that it needs to complete the current task.

This description is only a demonstration of the fundamental possibility of implementing such an interaction scheme and cannot be considered as a complete instruction for writing an image viewer, operating system and / or modules for it.

Previous articles in the series are available here: one , two , three , four. The full text, as before, is available on the project website .

All Articles