summaryrefslogtreecommitdiff
path: root/Mach/MachConcepts.mdwn
blob: b691a11931e5f18265e790eb2162cc22664d1e2c (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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
## <a name="Table_Of_Contents"> Table Of Contents </a>

%TOC%

A GNU Mach system consists of many _tasks_. 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. They are _resource containers_.

## <a name="Execution_time"> Execution time </a>

Tasks themselves don't spend execution time. The active entities in Mach are called _threads_ (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) for 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 _processors_ 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 _processor sets_. 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 _multicomputers_ build from _nodes_.

Threads have scheduling parameters and contain various statistics about them.

## <a name="Address_space"> Address space </a>

_Address space_ 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 _pages_. Each page has individual properties like _access rights_ (read/write/execute), _inheritance attributes_ (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 _memory objects_. The physical memory is conceived as a _memory cache_ that contains _memory cache objects_. 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 _memory managers_ that can be user tasks. The decision when they should be paged in or paged out is left to Mach. 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 someone 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.

_Wired pages_ are those that cannot be paged out. For example, Mach itself is a task with its own address space and threads, and all of its pages are wired.

_Precious pages_ are those that must not be discarded silently when they are clean 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.

## <a name="Communication_channels"> Communication channels </a>

_Communication channels_ in Mach are called _ports_. They can be compared with **nix system calls but have \*much** richer semantics and are ubiquitous in a Mach environment. In the Hurd, ports are used as _object references_. Hurd programs use these object references only by calling _methods_ defined in interface files (`.defs` files).

Ports themselves are queues of _messages_. 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 _port right_ 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 _port names_. They are integer numbers that form the _port name space_ of task. Ports are automatically destroyed when there is no associated port right to them.

So, the picture is that after obtaining a _port send right_, 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 _port receive right_ 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 _out-of-line data_. In the message they are references to memory regions which are _virtually copied_. 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 _reply port_ and it is usually referred by tasks with _send-once port right_. 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 (_marshalling_), wait for result on a newly created reply port, decode return arguments from the reply message (_unmarshalling_) 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 _port sets_. 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.3, 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 _NORMA_, not tested in GNU Mach) that do that.

More detailed information about GNU Mach interfaces can be found in [The GNU Mach Reference Manual](http://www.gnu.org/software/hurd/gnumach-doc/mach.html).

-- [[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

Convert from XHTML to [[TWiki/TextFormattingRules]]

-- [[Main/OgnyanKulev]] - 16 Jun 2003