blob: 61e84b094ee29dc24cab40e72587a72f7a130d17 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
## <a name="Mach_Concepts"> Mach Concepts </a>
A GNU Mach system consists of many <dfn>tasks</dfn>. You can think Mach tasks as \*nix processes but they are not the same. In processes there are signals, process/group/session IDs, file descriptors and many other things. Tasks are used for resource allocation and sharing. The main resources are:
* **_Execution time_**. Tasks themselves don't spend execution time. The active entities in Mach are called <dfn>threads</dfn> (points of execution). Each thread belongs to one and only one task and runs concurrently with all the other threads of a system. Tasks are resource environments (address space and communication channels) of threads.
Using threads is much more cheaper than using \*nix processes. Switching between threads in one task change processor registers' state but switching between threads in different tasks involves context switching (changing resource environment) which is slow on most computer architectures.
In a general case two threads of one task are able to run on two different <dfn>processors</dfn> simultaneously. That's the way Mach is designed. (Note that you can't play with the SMP support in GNU Mach yet.) Mach organizes processors into <dfn>processor sets</dfn>. A thread can be assigned to a processor set in order to be executed in any of the processors in the processor set. Of course, it is expected that all the processors in a processor set use shared memory. There is an optional and experimental support for building <dfn>multicomputers</dfn> build from <dfn>nodes</dfn>.
Threads have scheduling parameters and contain various statistics about them.
* **<dfn>Address space</dfn>** in Mach defines the valid addresses that can be used by threads in the task that owns that address space. Each task has only one address space and each address space belongs to only one task. So when we want to name an address space (e.g. in the Mach API) we name it by the task it belongs to.
Address space is divided into <dfn>pages</dfn>. Each page has individual properties like <dfn>access rights</dfn> (read/write/execute), <dfn>inheritance attributes</dfn> (no inheritance/copy/share) and some other system properties discussed later. Page manipulation is optimized to help moving large blocks of data from one address space to another (read: from a thread of task A to a thread of task B; further read: client/server technology).
Memory ranges of pages that can be controlled as a whole are called <dfn>memory objects</dfn>. The physical memory is conceived as a <dfn>memory cache</dfn> that contains <dfn>memory cache objects</dfn>. So when a thread accesses a page in its task's address space, the memory object that includes this page is cached in the memory cache. Memory objects are paged out and paged in by <dfn>memory managers</dfn> that can be user tasks. Each memory object has an ordered list of memory managers that provide paging. The last one tried is the default memory manager that resides in the microkernel. The others are generally user tasks. The default memory manager is needed because the microkernel can't wait infinitely something else to free the memory cache: it just calls the next memory manager hoping it to succeed.
As an example `mmap` POSIX interface (it maps file content to a memory region thus "loading" a file in no time) can be implemented by assigning a special memory manager to a memory range. When a thread accesses a page in that region our custom memory manager can load the corresponding block of the file.
<dfn>Wired pages</dfn> are those that cannot be paged out. For example Mach itself is a task with its address space and threads and all of its pages are wired.
<dfn>Precious pages</dfn> are those that must not be discarded silently when they are not modified and memory is needed. For example a memory manager that shares memory across a network could not restore a page if it is silently discarded because it is unmodified. This is not valid for the well-known pager managers that use disks as backing store.
* **<dfn>Communication channels</dfn>** in Mach are called <dfn>ports</dfn>. They can be compared with \*nix system calls but have **much** richer semantics and are ubiquitous in a Mach environment.
Ports themselves are queues of <dfn>messages</dfn>. There can be multiple senders and only one receiver of these messages -- ports are unidirectional communication channels. To send or receive a message a task must have corresponding <dfn>port right</dfn> to the port. (Of course a task can't execute code, it is the threads within it that do that.) Mach knows what port rights belong to each task but threads in tasks refer to ports by <dfn>port names</dfn>. They are integer numbers that form the <dfn>port name space</dfn> of a task. Ports are automatically destroyed when there is no associated port right to them.
So the picture is that after obtaining a <dfn>port send right</dfn>, the client uses a port name to send messages to the port. They are (probably) queued and when the server task tries to receive messages using its <dfn>port receive right</dfn> it gets the message(s).
Messages are not only opaque data. They can contain port rights to be passed to another task. Port rights are copied or moved. Notice that port receive right must be moved but not copied because there can't be more than one task that hold receive right to a port. The receiving task creates a new port name to the port right received.
Some data in the message can be <dfn>out-of-line data</dfn>. In the message they are references to memory regions which are <dfn>virtually copied</dfn>. When the message is received in a task these virtual copies become part of the address space of the task. "Virtual copy" means that it is not copied immediately but when it is changed. This is primarily used to send large blocks of data efficiently because it is too expensive to store them in the kernel address space.
The ability to send port rights to other tasks can be used to easily implement remote procedure calls (RPC) with return results and in/out arguments by sending a port right to which the result have to be returned. This is called <dfn>reply port</dfn> and it is usually referred by tasks with <dfn>send-once port right</dfn>. These port rights allow only one message to be send and after that the port right is immediately destroyed.
All these facilites are used by the _Mach Interface Generator (Mig)_ to provide easy RPC for Mach tasks. Procedure definitions are described in `.defs` files using a Mig-specific Interface Definition Languager (IDL). Then they are compiled by Mig to C stubs and skeletons that have to be compiled and linked to client and server programs respectively. After that client programs call remote procedures more or less like any other C function. These functions are implemented in the stubs and encode arguments into message (<dfn>marshalling</dfn>), wait for result on a newly created reply port, decode return arguments from the reply message (<dfn>unmarshalling</dfn>) and pass them to the client program. Similar actions are provided in the skeletons that are linked to server programs. Mig allows very precise semantics to be specified about what the arguments are and how to be passed. Unfortunately Mig can generate only C code.
When server task have to listen to a large number of ports using receive rights it can organize them into <dfn>port sets</dfn>. Port set look like port receive right but cannot be passed to another task and there are additional operations for including and excluding port receive rights. Waiting for a message from a port set waits all ports in the port set and returns exactly one message from randomly selected port that have message(s) in its queue. This is the only use of port sets.
Ports are kernel-protected resources. There is no way for a task to do anything with a port unless it have corresponding port right. Remember that threads do not deal with port rights directly -- they use port names which refer to port right in a task. The delivery of messages is reliable and strictly ordered -- when a thread sends messages 1 and 2 it is guaranteed that the receiving task will catch them in the same order (but there can be intermediate messages send by other threads).
**Controlling tasks, their address space, threads and other system objects** in Mach is implemented by using ports. Almost all of the Mach API (creating threads, etc) is implemented by sending messages to ports. **Device drivers** that reside in kernel space are controlled by ports too. In GNU Mach 1.2 these drivers are the Linux 2.0.36 ones. [[OskitMach]] provides more recent drivers.
Ports abstraction allows RPCs to be executed on another computer transparently. This can be implemented with user task but there is an implementation in the kernel (called <dfn>NORMA</dfn>, not tested in GNU Mach) that do that.
-- [[Main/OgnyanKulev]] - 09 Dec 2002
Here is a link to some Mach 3 (pre GNU) documents that might be of help circa 1992.
<http://ftp.cs.cmu.edu/afs/cs/project/mach/public/doc/osf/>
-- [[Main/GrantBow]] - 16 Dec 2002
|