|
UP
EROS Web Developer Documentation Programmer's Guide |
Run Time EnvironmentD R A F T
If you are trying to write real programs, it's useful to know what the state of the world is when the program begins excution. This page provides a description of how the usual EROS runtime mechanism works. The EROS kernel is not responsible for process construction; it is therefore not a party to any startup conventions about these programs. It is possible to build many different startup conventions. This is useful primarily for the sake of environment emulation. The description given here describes the startup and shutdown conventions for native EROS programs. More specifically, this page describes the convention that is assumed by the EROS Constructor. There are a few primordial processes that implement these conventions a bit differently. Briefly, the
programmer's view of process startup is fairly
straightforward. When the program's 1. Process FabricationEROS programs are usually built by constructors, and the construction process if typically recursive. In the common case, even the process address space is constructed by another constructor. This leads to apparently conflicting objectives:
To resolve this conflict, process fabrication is done by a temporary program known as the protospace from within the new process itself. To minimize confusion, imagine that the program we are trying to fabricate will be called foo, and the address space for this program will be called foospace. 1.1 ConstructionThe first stage is to construct the shell of the new process and populate it with the protospace program. This proceeds as follows:
1.2 Protospace InitializationProtospace must now construct the initial address space and keeper for the new process:
Note that protospace makes use of key registers during startup. When building an image map, programmers should not place capabilities that the program will need into capability registers. They should go in the constituents node. 1.3 New Process StartupOn entry, the new process inherits the key register state that was left by the protospace. This is identical to the initial key registers described above, except that register KR7 and KR8 hold zero number keys. Depending on the type of address space used for this process, the process may initially have no stack or modifiable data. Three kinds of spaces exist in the current system:
The run-time library determines the type of the address
space by examining the global variable
If necessary, the process must arrange to call on it's own behalf any constructors whose products it may need. 2. Process TeardownJust as the process must arrange to initialize itself, it must arrange to destroy itself if it honors the OC_Destroy request. Since the process created it's own address space, it must destroy it. careful look at the object reference manual will show that the destroy operation is expected to return. The question is, ``How can a program that no longer has an address space return to anyone?'' 2.1 Process FinalizationA process that wishes to honor the OC_Destroy order code must hang on to it's process creator key. If the process runs in a kept address space, it must also retain access to the protospace page.
When asked to destroy itself, the process should first
destroy (recursively) any services it has built. It
should then arrange to jump back into the protospace
address space for the last phase of the destruction by
calling
On entry to
How shutdown is accomplished depends on the process address space:
The
The process creator now gets control, and demolishes the process structure. Finally, it returns to the designated resume key, confirming to the original caller (the one who asked the foo process to self-destruct) that the foo process has been successfully destroyed. Note that protospace itself is a shared resource that does not get destroyed. 2.2 CaveatsThere are some circumstances under which the destroy request may fail:
Destruction is therefore done on a ``best effort'' basis. A client who wishes to unconditionally ensure that it's space can be reclaimed should arrange to fabricate its services under a separate subspace bank, and destroy the entire subspace. Note that doing so does not permit the services to perform finalization, and should be viewed as a way of recovering from buggy or pernicious services rather than as a way of tearing down programs in general. 3. Small and Normal Process ModelsThere are two primary runtime models for EROS processes: normal and ``small''. All EROS platforms support both process models. The normal model provides a fairly complete ``cocoon'' for the process. This relieves the application designer of the need to worry about page fault handling or stack/heap collision, but it comes at a cost: the application is required to have a memory keeper that will service page faults for the heap and the stack. The small model exists to provide a low-overhead execution environment for processes that have only a small amount of state, and also to provide a minimal-demand runtime environment that can be used to implement the richer environment. Low overhead comes at the cost of correspondingly reduced runtime support. Surprisingly, we find that many processes run very happily using the small runtime model. Where feasible, use of the small model is preferred, because small model processes have lower startup overheads. On some architectures, small processes also provide more efficient entry/exit costs during interprocess communication (start and resume key invocation). As of this writing, the differences between the two models come down to two issues:
At the implementation level, these issues are entirely independent: one concerns the behavior of the application runtime library. The other concerns favoritism applied to certain procsses by the kernel. Typically, however, we find that processes are concerned about both or neither of these issues. Since the small space issue is simpler, we will deal with that first. 3.1 Small Address SpacesOn some architectures, notably the x86, the kernel provides preferred support for processes that use only a small amount of virtual address space. Specifically, any process that restricts itself to virtual addresses in the range [0, 32*PageSize-1] will be mapped by the kernel into a so-called small space. Small spaces take advantage of some dirty segmenting tricks to be simultaneously mapped in all address spaces, which leads to significantly improved context switch efficiency. Programmers writing performance-critical code may wish to take advantage of this by writing programs that meet the small space constraint. Under the covers, the kernel actually runs every process as a small process until the first time the process violates the small process addressing constraints. That is: processes are assumed to be small until a reference at a higher virtual address is actually observed. At that point, the process is graduated to large procss status. While all of this is transparent to the application, authors of performance-critical applications may find it helpful to know where the magic addressing threshold is. The 32 page restriction is motivated by internal implementation convenience in the kernel. Future kernels may provide greater flexibility in this regard. In order to play nicely with the small space optimization, the ``small process'' runtime library typically sets up the process stack to begin at 128k and grow downward. 3.2 Memory AllocationMost programs find it convenient to assume that memory allocation will be handled transparently by a memory keeper. Typically, these programs are fabricated as ``virtual copies'' (copied via copy on write) of some original program image. The first time an existing page is modified, a private copy of that page is made for the application. Similarly, when a previously undefined address is first read, a new, empty (zero) page is allocated ``on demand'' for that location. This model is convenient, because heap and stack growth are transparently handled, but this convenience comes at a cost. A per-address-space memory keeper must exist, and this keeper must be instantiated before the program runs. To minimize overhead, some programs may find it more appropriate to manage their own memory. This is actually important enough that some support for it exists in the core runtime libraries. Self-managed memory raises three concerns:
The runtime library deals with the memory management issues via three variables that can be overridden by the application as needed. The three variables and their defaults in each runtime model are shown below:
Each of these symbols is defined as a weak symbol by the standard runtime libraries. The normal case values are defined in crtn.o. Applications built with -lsmall receive overridden versions of these variables corresponding to the values shown for the small runtime above. During it's lifetime, an application may well switch from one mode to another. For example, an initially small-model application may allocate a kept heap area, set up the heap there, and change the value of __rt_kept_heap accordingly. Mechanisms for managing heaps in this way are included in the -lselfheap library. Programmers are advised that the heap support routines compiled into the -lsmall library presume that the application has been created using mkimage as a small process. The functions provided in the conventional runtime library are more general. Copyright 2001 by Jonathan Shapiro & K. Johansen. All rights reserved. For terms of redistribution, see the EROS License Agreement |