|
|
< Previous PageNext Page > |
Mach IPC and Mach messaging are the basis for much of the communication in Mac OS X. In many cases, however, these facilities are used indirectly by services implemented on top of one of them. Mach messaging and IPC are fundamentally similar except that Mach messaging is stateless, which prevents certain types of error recovery, as explained later. Except where explicitly stated, this section treats the two as equivalent.
The fundamental unit of Mach IPC is the port. The concept of Mach ports can be difficult to explain in isolation, so instead this section assumes a passing knowledge of a similar concept, that of ports in TCP/IP.
In TCP/IP, a server listens for incoming connections over a network on a particular port. Multiple clients can connect to the port and send and receive data in word-sized or multiple-word–sized blocks. However, only one server process can be bound to the port at a time.
In Mach IPC, the concept is the same, but the players are different. Instead of multiple hosts connecting to a TCP/IP port, you have multiple Mach tasks on the same computer connecting to a Mach port. Instead of firewall rules on a port, you have port rights that specify what tasks can send data to a particular Mach port.
Also, TCP/IP ports are bidirectional, while Mach ports are unidirectional, much like UNIX pipes. This means that when a Mach task connects to a port, it generally allocates a reply port and sends a message containing send rights to that reply port so that the receiving task can send messages back to the sending task.
As with TCP/IP, multiple client tasks can open connections to a Mach port, but only one task can be listening on that port at a time. Unlike TCP/IP, however, the IPC mechanism itself provides an easy means for one task to hand off the right to listen to an arbitrary task. The term receive rights refers to a task’s ability to listen on a given port. Receive rights can be sent from task to task in a Mach message. In the case of Mach IPC (but not Mach messaging), receive rights can even be configured to automatically return to the original task if the new task crashes or becomes unreachable (for example, if the new task is running on another computer and a router crashes).
In addition to specifying receive rights, Mach ports can specify which tasks have the right to send data. A task with send rights may be able to send once, or may be able to arbitrarily send data to a given port, depending on the nature of the rights.
Using Well-Defined Ports
Remote Procedure Calls (RPC)
Calling RPC From User Applications
Before you can use Mach IPC for task communication, the sending task must be able to obtain send rights on the receiving task’s task port. Historically, there are several ways of doing this, not all of which are supported by Mac OS X. For example, in Mac OS X, unlike most other Mach derivatives, there is no service server or name server. Instead, the bootstrap task and mach_init subsume this functionality.
When a task is created, it is given send rights to a bootstrap port for sending messages to the bootstrap task. Normally a task would use this port to send a message that gives the bootstrap task send rights on another port so that the bootstrap task can then return data to the calling task. Various routines exist in bootstrap.h
that abstract this process. Indeed, most users of Mach IPC or Mach messaging actually use Mach remote procedure calls (RPC), which are implemented on top of Mach IPC.
Since direct use of IPC is rarely desirable (because it is not easy to do correctly), and because the underlying IPC implementation has historically changed on a regular basis, the details are not covered here. You can find more information on using Mach IPC directly in the Mach 3 Server Writer’s Guide from Silicomp (formerly the Open Group, formerly the Open Software Foundation Research Institute), which can be obtained from the developer section of Apple’s website. While much of the information contained in that book is not fully up-to-date with respect to Mac OS X, it should still be a relatively good resource on using Mach IPC.
Mach RPC is the most common use for Mach IPC. It is frequently used for user-kernel communication, but can also be used for task to task or even computer-to-computer communication. Programmers frequently use Mach RPC for setting certain kernel parameters such as a given thread’s scheduling policy.
RPC is convenient because it is relatively transparent to the programmer. Instead of writing long, complex functions that handle ports directly, you have only to write the function to be called and a small RPC definition to describe how to export the function as an RPC interface. After that, any application with appropriate permissions can call those functions as if they were local functions, and the compiler will convert them to RPC calls.
In the directory osfmk/mach
(relative to your checkout of the xnu module from CVS), there are a number of files ending in .defs
; these files contain the RPC definitions. When the kernel (or a kernel module) is compiled, the Mach Interface Generator (MIG) uses these definitions to create IPC code to support the functions exported via RPC. Normally, if you want to add a new remote procedure call, you should do so by adding a definition to one of these existing files. (See “Building and Debugging Kernels” for more information on obtaining kernel sources.)
What follows is an example of the definition for a routine, one of the more common uses of RPC.
routine thread_policy_get( |
thread : thread_act_t; |
flavor : thread_policy_flavor_t; |
out policy_info : thread_policy_t, CountInOut; |
inout get_default : boolean_t); |
Notice the C-like syntax of the definition. Each parameter in the routine roughly maps onto a parameter in the C function. The C prototype for this function follows.
kern_return_t thread_policy_get( |
thread_act_t act, |
thread_policy_flavor_t flavor, |
thread_policy_t policy_info, |
mach_msg_type_number_t *count, |
boolean_t get_default); |
The first two parameters are integers, and are passed as call-by-value. The third is a struct
containing integers. It is an outgoing parameter, which means that the values stored in that variable will not be received by the function, but will be overwritten on return.
Note: The parameters are all word-sized or multiples of the word size. Smaller data are impossible because of limitations inherent to the underlying Mach IPC mechanisms.
From there it becomes more interesting. The fourth parameter in the C prototype is a representation of the size of the third. In the definition file, this is represented by an added option, CountInOut
.
The MIG option CountInOut
specifies that there is to be an inout
parameter called count
. An inout
parameter is one in which the original value can be read by the function being called, and its value is replaced on return from that function. Unlike a separate inout
parameter, however, the value initially passed through this parameter is not directly set by the calling function. Instead, it is tied to the policy_info
parameter so that the number of integers in policy_info
is transparently passed in through this parameter.
In the function itself, the function checks the count parameter to verify that the buffer size is at least the size of the data to be returned to prevent exceeding array bounds. The function changes the value stored in count to be the desired size and returns an error if the buffer is not large enough (unless the buffer pointer is null, in which case it returns success). Otherwise, it dereferences the various fields of the policy_info
parameter and in so doing, stores appropriate values into it, then returns.
Note: Since Mach RPC is done via message passing, inout
parameters are technically call-by-value-return and not call-by-reference. For more realistic call-by-reference, you need to pass a pointer. The distinction is not particularly significant except when aliasing occurs. (Aliasing means having a single variable visible in the same scope under two or more different names.)
In addition to the routine
, Mach RPC also has a simpleroutine. A simpleroutine
is a routine that is, by definition, asynchronous. It can have no out
or inout
parameters and no return value. The caller does not wait for the function to return. One possible use for this might be to tell an I/O device to send data as soon as it is ready. In that use, the simpleroutine
might simply wait for data, then send a message to the calling task to indicate the availability of data.
Another important feature of MIG is that of the subsystem. In MIG, a subsystem is a group of routines
and simpleroutines
that are related in some way. For example, the semaphore subsystem contains related routines that operate on semaphores. There are also subsystems for various timers, parts of the virtual memory (VM) system, and dozens of others in various places throughout the kernel.
Most of the time, if you need to use RPC, you will be doing it within an existing subsystem. The details of creating a new subsystem are beyond the scope of this document. Developers needing to add a new Mach subsystem should consult the Mach 3 Server Writer’s Guide from The Open Group (TOG), which can be obtained from various locations on the internet.
Another feature of MIG is the type. A type in MIG is exactly the same thing as it is in programming languages. However, the construction of aggregate types differs somewhat.
type clock_flavor_t = int; |
type clock_attr_t = array[*:1] of int; |
type mach_timespec_t = struct[2] of int; |
Data of type array
is passed as the user-space address of what is assumed to be a contiguous array of the base type, while a struct is passed by copying all of the individual values of an array of the base type. Otherwise, these are treated similarly. A “struct” is not like a C struct, as elements of a MIG struct must all be of the same base type.
The declaration syntax is similar to Pascal, where *:1
and 2
represent sizes for the array or structure, respectively. The *:1
construct indicates a variable-sized array, where the size can be up to 1, inclusive, but no larger.
RPC, as mentioned previously, is virtually transparent to the client. The procedure call looks like any other C function call, and no additional library linkage is needed. You need only to bring the appropriate headers in with a #include
directive. The compiler automatically recognizes the call as a remote procedure call and handles the underlying MIG aspects for you.
< Previous PageNext Page > |
Last updated: 2006-11-07
|
Get information on Apple products.
Visit the Apple Store online or at retail locations. 1-800-MY-APPLE Copyright © 2007 Apple Inc. All rights reserved. | Terms of use | Privacy Notice |