Section 9.6. MIG


9.6. MIG

Informally speaking, the phrase remote procedure call (RPC) denotes a mechanism that allows programs to call procedures transparently with respect to the procedures' locations. In other words, using RPC, a program can call a remote procedure, which may reside within another program or even within a program on another computer.

The Remote Past of RPC

Jim E. White described an alternative approach to network-based resource sharing in a paper (RFC 707) in 1975.[10] White's approach was based on having a framework that required a common command/response discipline that was independent of any specific network application. His paper described what was perhaps the first formal RPC mechanism. Instead of reimplementing the network runtime environment as part of every application, the programmer could execute a procedure with the specified arguments on a remote machine, much like a usual (local) procedure call. Numerous RPC systems have been built since White's paper, such as Xerox Courier (1981), Sun RPC (1985), and OSF Distributed Computing Environment (DCE, 1991). Examples of relatively recent systems include XML RPC and Simple Object Access Protocol (SOAP).


[10] "A High-Level Framework for Network-Based Resource Sharing" (RFC 707), by Jim E. White (The Internet Engineering Task Force, December 1975).

Most RPC systems include tools that ease the programmer's job by taking care of repetitive, tedious, and mechanical aspects of RPC programming. For example, Sun RPC provides the rpcgen program, which compiles an RPC specification file to generate C language code that can be linked with other C code explicitly written by the programmer. The specification filea .x filedefines server procedures, their arguments, and their results.

Mach Interface Generator (MIG) is a tool[11] that generates RPC code for client-server-style Mach IPC from specification files. Since typical IPC programs perform similar operations of preparing, sending, receiving, unpacking, and demultiplexing messages, MIG is able to automatically generate code for these operations based on programmer-provided specifications of message passing and procedure call interfaces. Automated code generation also promotes consistency and reduces the likelihood of programming errors. Besides, if the programmer wishes to change the interface, only the appropriate specification file needs to be modified.

[11] Another term for tools such as MIG is stub generatorsMIG generates client stubs for Mach IPC.

A Ma(t)ch Made . . .

MIG originally implemented a subset of a language called Matchmaker, which was also intended for specifying and automating the generation of IPC interfaces. Matchmaker was meant to be multilingual: It generated C, Pascal, and Lisp code at different times during its evolution. In fact, the syntax of MIG declarations still resembles Pascal syntax, although MIG generates only C code.

Depending on the specific RPC system, tools similar to MIG may or may not hide features of the underlying IPC layer from the programmerhowever, MIG does not.


9.6.1. MIG Specification Files

A MIG specification file conventionally has the .defs extension. MIG processes a .defs file to generate the following three files:

  • A header file for inclusion by client code

  • A user-interface module to be linked with client codecontains functions for sending request messages to the server and receiving replies

  • A server-interface module to be linked with server codecontains functions for receiving requests from the client, for calling the appropriate server function (programmer-provided) based on the contents of the request message, and for sending reply messages

A MIG specification file contains the following types of sections, not all of which are mandatory:

  • Subsystem identifier

  • Serverdemux declaration

  • Type specifications

  • Import declarations

  • Operation descriptions

  • Options declarations

A MIG "subsystem" is a collective name for a client, the server called by the client, and the set of operations exported by the server. The subsystem keyword names the MIG subsystem specified by the file. MIG use this identifier as a prefix in the names of the code files it generates.

subsystem          system-name        message-base-id ;


The subsystem keyword is followed by the ASCII name (e.g., foo) of the subsystem being defined. message-base-id is the integer base value used as the IPC message identifier (the msgh_id field in the message header) of the first operation in the specification file. In other words, this value is the base beginning with which operations are numbered sequentially. message-base-id may be arbitrarily chosen. However, if the same program serves multiple interfaces, then each interface must have a unique identifier so that the server can unambiguously determine the operations invoked.

When MIG creates a reply message corresponding to a request message, the reply identifier is conventionally the sum of the request identifier and the number 100.


The serverdemux declaration section can be used to specify an alternative name for the server demultiplexing routine in the server-interface module. The demultiplexing routine examines the request message, calling the appropriate subsystem routine based on the msgh_id value in the message header. If the value is out of bounds for the subsystem, the demultiplexing routine returns an error. The default name for this routine is <system-name>_server, where <system-name> is the name specified through the subsystem statement.

serverdemux    somethingelse_server ;


The type specifications section is used for defining data types corresponding to parameters of the calls exported by the user-interface module. MIG supports declarations for types such as simple, structured, pointer, and polymorphic.

/*  * Simple Types  * type type-name = type-description;  */ type int           = MACH_MSG_TYPE_INTEGER_32; type kern_return_t = int; type some_string   = (MACH_MSG_TYPE_STRING, 8*128); /*  * Structured and Pointer Types  * type type-name = array [size] of type-description;  * type type-name = array [*:maxsize] of type-description;  * struct [size] of type-description;  * type type-name = ^ type-description;  */ type thread_ids   = array[16] of MACH_MSG_TYPE_INTEGER_32; type a_structure  = struct[16] of array[8] of int; type ool_array    = ^ array[] of MACH_MSG_TYPE_INTEGER_32; type intptr       = ^ MACH_MSG_TYPE_INTEGER_32; type input_string = array[*:64] of char;


A polymorphic type is used to specify an argument whose exact type is not determined until runtimea client must specify the type information as an auxiliary argument at runtime. MIG automatically includes an additional argument to accommodate this. Consider the following simple definition file:

/* foo.defs */ subsystem foo 500 #include <mach/std_types.defs> #include <mach/mach_types.defs> type my_poly_t = polymorphic; routine foo_func(             server : mach_port_t;             arg    : my_poly_t);


The MIG-generated code for foo_func() has the following prototype:

kern_return_t foo_func(mach_port_t          server,          my_poly_t            arg,          mach_msg_type_name_t argPoly);


A type declaration can optionally contain information specifying procedures for translating or deallocating types. Translation allows a type to be seen differently by the user- and server-interface modules. A deallocation specification allows a destructor function to be specified. In Section 9.6.2 we will see an example involving translation and deallocation specifications.

Import declarations are used to include header files in MIG-generated modules. MIG can be directed to include such headers in both the user- and server-interface modules, or in only one of the two.

/*  * import header-file;  * uimport header-file;  * simport header-file;  */ import "foo.h";          /* imported in both modules */ uimport <stdlib.h>;      /* only in user-interface module */ simport <stdio.h>;       /* only in server-interface module */


The operations section contains specifications for one or more types of IPC operations. The specification includes a keyword for the kind of operation being described, the name of the operation, and the names and types of its arguments. When MIG compiles the specification file, it generates client and server stubs for each operation. A client stub resides in the user-interface module. Its job is to package and send the message corresponding to a procedure call invocation in the client program. The server stub resides in the server-interface module. It unpacks received messages and calls the programmer's server code that implements the operation.

Operation types supported by MIG include Routine, SimpleRoutine, Procedure, SimpleProcedure, and Function. Table 92 shows the characteristics of these types.

Table 92. Operation Types Supported by MIG

Operation Type

Reply Received?

Error Returned?

Routine

Yes

Yes, a kern_return_t return value specifying whether the operation was successfully completed

SimpleRoutine

No

Yes, the return value from Mach's message-sending primitive

Procedure

Yes

No

SimpleProcedure

No

No

Function

Yes

No error code returned, but a value from the server function is returned


The following is an example of an operation specification:

routine vm_allocate(                 target_task     : vm_task_entry_t;         inout   address         : vm_address_t;                 size            : vm_size_t;                 flags           : int);


A parameter specification contains a name and a type and may optionally be adorned by one of the keywords in, out, or inout, representing that the argument is only sent to the server, is sent by the server on its way out, or both, respectively.

In the operations section, the skip keyword causes MIG to skip assignment of the next operation ID, resulting in a hole in the sequence of operation IDs. This can be useful to preserve compatibility as interfaces evolve.


The options declarations section is used for specifying special-purpose or global options that affect the generated code. The following are examples of options:

  • WaitTime used for specifying the maximum time in milliseconds that the user-interface code will wait for receiving a reply from the server

  • MsgType used for setting the message type (e.g., to mark messages as being encrypted)

  • UserPrefix used for specifying a string that will be a prefix of client-side function names that call IPC operations

  • ServerPrefix used for specifying a string that will be a prefix of server-side function names that implement IPC operations

  • Rcsid used for specifying a string that will cause static string variables called Sys_server_rcsid and Sys_user_rcsid to be declared in the server and user modules, respectively, with their constant values each being the specified string

Numerous examples of MIG specification files exist in /usr/include/mach/ and its subdirectories.


9.6.2. Using MIG to Create a Client-Server System

Let us use MIG to create a simple client-server system. A MIG server is a Mach task that provides services to its clients using a MIG-generated RPC interface. Our MIG server will serve two routines: one to calculate the length of a string sent by the client and another to calculate the factorial of a number sent by the client. In our example, the client will send the string inline, and the server will send only simple integers. Recall that when an interface call returns out-of-line data, it is the caller's responsibility to deallocate the memory by calling vm_deallocate(). For example, we could add another operation to our interface, say, one that reverses the string sent by the client and returns the reversed string by allocating memory for it in the caller's address space.

We call our MIG server the Miscellaneous Server. Its source consists of the following four files:

  • A header file containing useful definitions and prototypes used by both the client and the server (misc_types.h)

  • The MIG specification file (misc.defs)

  • Setup and main loop for the server (server.c)

  • Demonstration of the interface (client.c)

Figure 926 shows the common header file. We define two new data types: input_string_t, which is a character array 64 elements in size, and xput_number_t, which is another name for an integer.

Figure 926. Common header file for the Miscellaneous Server and its client

// misc_types.h #ifndef _MISC_TYPES_H_ #define _MISC_TYPES_H_ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <mach/mach.h> #include <servers/bootstrap.h> // The server port will be registered under this name. #define MIG_MISC_SERVICE "MIG-miscservice" // Data representations typedef char input_string_t[64]; typedef intxput_number_t; typedef struct {     mach_msg_header_t head;     // The following fields do not represent the actual layout of the request     // and reply messages that MIG will use. However, a request or reply     // message will not be larger in size than the sum of the sizes of these     // fields. We need the size to put an upper bound on the size of an     // incoming message in a mach_msg() call.     NDR_record_t NDR;     union {         input_string_t string;         xput_number_tnumber;     } data;     kern_return_t      RetCode;     mach_msg_trailer_t trailer; } msg_misc_t; xput_number_t misc_translate_int_to_xput_number_t(int); int           misc_translate_xput_number_t_to_int(xput_number_t); void          misc_remove_reference(xput_number_t); kern_return_t string_length(mach_port_t, input_string_t, xput_number_t *); kern_return_t factorial(mach_port_t, xput_number_t, xput_number_t *); #endif // _MISC_TYPES_H_

Figure 927 shows the specification file. Note the type specification of xput_number_t. Each MIG type can have up to three corresponding C types: a type for the user-interface module (specified by the CUserType option), a type for the server module (specified by the CServerType option), and a translated type for internal use by server routines. The CType option can be used in place of CUserType and CServerType if both types are the same. In our case, the CType option specifies the C data type for the MIG type xput_number_t.

Figure 927. MIG specification file for the Miscellaneous Server

/*  * A "Miscellaneous" Mach Server  */ /*  * File:    misc.defs  * Purpose: Miscellaneous Server subsystem definitions  */ /*  * Subsystem identifier  */ Subsystem misc 500; /*  * Type declarations  */ #include <mach/std_types.defs> #include <mach/mach_types.defs> type input_string_t = array[64] of char; type xput_number_t  = int          CType      : int          InTran     : xput_number_t misc_translate_int_to_xput_number_t(int)          OutTran    : int misc_translate_xput_number_t_to_int(xput_number_t)          Destructor : misc_remove_reference(xput_number_t)     ; /*  * Import declarations  */  import "misc_types.h"; /*  * Operation descriptions  */ /* This should be operation #500 */ routine string_length(                          server_port : mach_port_t;                       in instring    : input_string_t;                      out len         : xput_number_t); /* Create some holes in operation sequence */ Skip; Skip; Skip; /* This should be operation #504, as there are three Skip's */ routine factorial(                      server_port : mach_port_t;                   in num         : xput_number_t;                  out fac         : xput_number_t); /*  * Option declarations  */ ServerPrefix Server_; UserPrefix   Client_;

We use the InTran, OutTran, and Destructor options to specify procedures that we will provide for translation and deallocation. Translation is useful when a type must be seen differently by the server and the client. In our example, we want the type in question to be an xput_number_t for the server and an int for the client. We use InTran to specify misc_translate_int_to_xput_number_t() as the incoming translation routine for the type. Similarly, misc_translate_xput_number_t_to_int() is the outgoing translation routine. Since xput_number_t is actually just another name for an int in our case, our translation functions are trivial: They simply print a message.

Real-life translation functions can be arbitrarily complex. The kernel makes heavy use of translation functions. See Section 9.6.3 for an example.


We also use the Destructor option to specify a deallocation function that MIG will call at the appropriate time.

Figure 928 shows the source for the server.

Figure 928. Programmer-provided source for the Miscellaneous Server

// server.c #include "misc_types.h" static mach_port_t server_port; extern boolean_t misc_server(mach_msg_header_t *inhdr,                              mach_msg_header_t *outhdr); void server_setup(void) {     kern_return_t kr;    if ((kr = bootstrap_create_service(bootstrap_port, MIG_MISC_SERVICE,                                       &server_port)) != BOOTSTRAP_SUCCESS) {         mach_error("bootstrap_create_service:", kr);         exit(1);     }     if ((kr = bootstrap_check_in(bootstrap_port, MIG_MISC_SERVICE,                                  &server_port)) != BOOTSTRAP_SUCCESS) {         mach_port_deallocate(mach_task_self(), server_port);         mach_error("bootstrap_check_in:", kr);         exit(1);     } } void server_loop(void) {     mach_msg_server(misc_server,            // call the server-interface module                     sizeof(msg_misc_t),     // maximum receive size                     server_port,            // port to receive on                     MACH_MSG_TIMEOUT_NONE); // options } // InTran xput_number_t misc_translate_int_to_xput_number_t(int param) {      printf("misc_translate_incoming(%d)\n", param);      return (xput_number_t)param; } // OutTran int misc_translate_xput_number_t_to_int(xput_number_t param) {      printf("misc_translate_outgoing(%d)\n", (int)param);      return (int)param; } // Destructor void misc_remove_reference(xput_number_t param) {      printf("misc_remove_reference(%d)\n", (int)param); } // an operation that we export kern_return_t string_length(mach_port_t     server_port,               input_string_t  instring,               xput_number_t  *len) {     char *in = (char *)instring;     if (!in || !len)         return KERN_INVALID_ADDRESS;     *len = 0;     while (*in++)         (*len)++;     return KERN_SUCCESS; } // an operation that we export kern_return_t factorial(mach_port_t server_port, xput_number_t num, xput_number_t *fac) {     int i;     if (!fac)         return KERN_INVALID_ADDRESS;     *fac = 1;     for (i = 2; i <= num; i++)         *fac *= i;     return KERN_SUCCESS; } int main(void) {     server_setup();     server_loop();     exit(0); }

Figure 929 shows the programmer-provided source for the client we will use to call the Miscellaneous Server interface routines.

Figure 929. A client for accessing the services provided by the Miscellaneous Server

// client.c #include "misc_types.h" #define INPUT_STRING "Hello, MIG!" #define INPUT_NUMBER 5 int main(int argc, char **argv) {     kern_return_t kr;     mach_port_t   server_port;     int           len, fac;     // look up the service to find the server's port     if ((kr = bootstrap_look_up(bootstrap_port, MIG_MISC_SERVICE,                                 &server_port)) != BOOTSTRAP_SUCCESS) {         mach_error("bootstrap_look_up:", kr);         exit(1);     }     // call a procedure     if ((kr = string_length(server_port, INPUT_STRING, &len)) != KERN_SUCCESS)         mach_error("string_length:", kr);     else         printf("length of \"%s\" is %d\n", INPUT_STRING, len);     // call another procedure     if ((kr = factorial(server_port, INPUT_NUMBER, &fac)) != KERN_SUCCESS)         mach_error("factorial:", kr);     else         printf("factorial of %d is %d\n", INPUT_NUMBER, fac);     mach_port_deallocate(mach_task_self(), server_port);     exit(0); }

Next, we must run the mig program on the specification file. As noted earlier, doing so will give us a header file (misc.h), a user-interface module (miscUser.c), and a server-interface module (miscServer.c). As Figure 930 shows, we compile and link together client.c and miscUser.c to yield the client program. Similarly, server.c and miscServer.c yield the server program.

Figure 930. Creating a MIG-based client and server system


$ ls -m client.c, misc.defs, misc_types.h, server.c $ mig -v misc.defs Subsystem misc: base = 500 Type int8_t = (9, 8) Type uint8_t = (9, 8) ... Type input_string_t = array [64] of (8, 8) Type xput_number_t = (2, 32)         CUserType:      int         CServerType:    int         InTran:         xput_number_t misc_translate_int_to_xput_number_t(int)         OutTran:        int misc_translate_xput_number_t_to_int(xput_number_t)         Destructor:     misc_remove_reference(xput_number_t) Import "misc_types.h" Routine (0) string_length(         RequestPort     server_port: mach_port_t         In              instring: input_string         Out             len: xput_number) Routine (4) factorial(         RequestPort     server_port: mach_port_t         In              num: xput_number         Out             fac: xput_number) ServerPrefix Server_ UserPrefix Client_ Writing misc.h ... done. Writing miscUser.c ... done. Writing miscServer.c ... done. $ ls -m client.c, misc.defs, misc.h, miscServer.c, miscUser.c, misc_types.h, server.c $ gcc -Wall -o server server.c miscServer.c $ gcc -Wall -o client client.c miscUser.c $ ./server


Once the server is running, we can also use our bootstrap_info program to verify that our service's name (MIG-miscservice, as defined in misc_types.h) is listed.

$ bootstrap_info ... 1   MIG-miscservice                                                 - $ ./client length of "Hello, MIG!" is 11 factorial of 5 is 120


Figure 931 shows the sequence of actions that occur when the client calls the server's string_length() operation.

Figure 931. Invocation of Miscellaneous Server routines by a client


9.6.3. MIG in the Kernel

MIG is used to implement most Mach system calls. Several system calls, such as task-related, IPC-related, and VM-related calls, take a target task as one of their arguments. MIG translates the task argument in each case depending on the kernel-facing data type. For example, a Mach thread is seen as a port name in user space, but inside the kernel, MIG calls convert_port_to_thread() [osfmk/kern/ipc_tt.c] to translate an incoming thread port name to a pointer to the kernel object represented by the porta thread structure.

/* osfmk/mach/mach_types.defs */ type thread_t = mach_port_t #if     KERNEL_SERVER                 intran:     thread_t convert_port_to_thread(mach_port_t)                 outtran:    mach_port_t convert_thread_to_port(thread_t)                 destructor: thread_deallocate(thread_t) #endif  /* KERNEL_SERVER */


Note the KERNEL_SERVER conditional directive. The Mac OS X kernel uses it, and a related directive KERNEL_USER, in MIG specification files to specify the KernelServer and KernelUser subsystem modifiers.

/* osfmk/mach/task.defs */ subsystem #if KERNEL_SERVER     KernelServer #endif /* KERNEL_SERVER */     task 3400;


The subsystem modifiers instruct MIG to generate alternate code for the user and server modules for special environments. For example, when a MIG server routine resides in the kernel, it is said to be in the KernelServer environment. Although the routine will have the same prototype as it would without the KernelServer modifier, the latter changes how MIG performs type translation. A mach_port_t type is automatically converted to the kernel type ipc_port_t on the KernelServer subsystem's server side.

9.6.3.1. Interfaces to Kernel Objects

Mach not only uses ports to represent several types of kernel objects but also exports interfaces to these objects through Mach IPC. Such interfaces are also implemented using MIG. User programs can use these interfaces either directly via Mach IPC or, as is typically the case, by calling standard library functions. When the system library is compiled, it links in user-interface modules corresponding to several kernel object MIG definition files. The library build process runs mig on the definition files to generate the interface modules.

Examples of kernel object types include thread, task, host, processor, processor set, memory object, semaphore, lock set, and clock. A complete list of defined kernel object types is in osfmk/kern/ipc_kobject.h.


9.6.3.2. MIG Initialization in the Kernel

The kernel maintains a mig_subsystem structure [osfmk/mach/mig.h] for the MIG subsystem corresponding to each type of kernel object. As the IPC subsystem is initialized during kernel startup, the MIG initialization functionmig_init() [osfmk/kern/ipc_kobject.c]iterates over each subsystem, populating a global hash table of MIG routines. Figure 932 shows an excerpt from this process.

Figure 932. Initialization of MIG subsystems during kernel bootstrap

// osfmk/mach/mig.h typedef struct mig_subsystem {     mig_server_routine_t   server;     // pointer to demux routine     mach_msg_id_t          start;      // minimum routine number     mach_msg_id_t          end;        // maximum routine number + 1     mach_msg_size_t        maxsize;    // maximum reply message size     vm_address_t           reserved;   // reserved for MIG use     mig_routine_descriptor routine[1]; // routine descriptor array } *mig_subsystem_t; // osfmk/kern/ipc_kobject.c typedef struct {     mach_msg_id_t  num;     mig_routine_t  routine;     int            size; #if MACH_COUNTERS     mach_counter_t callcount; #endif } mig_hash_t; #define MAX_MIG_ENTRIES    1024 mig_hash_t mig_buckets[MAX_MIG_ENTRIES]; const struct mig_subsystem* mig_e[] = {     (const struct mig_subsystem *)&mach_vm_subsystem,     (const struct mig_subsystem *)&mach_port_subsystem,     (const struct mig_subsystem *)&mach_host_subsystem,     ...     (const struct mig_subsystem *)&is_iokit_subsystem),     ... }; void mig_init(void) {     unsigned int i, n = sizeof(mig_e)/sizeof(const struct mig_subsystem *);     int howmany;     mach_msg_id_t j, pos, nentry, range;     for (i = 0; i < n; i++) { // for each mig_e[i]         range = mig_e[i]->end - mig_e[i]->start;         ...         for (j = 0; j < range; j++) { // for each routine[j] in mig_e[i]             ...             // populate mig_buckets hash table with routines         }     } }




Mac OS X Internals. A Systems Approach
Mac OS X Internals: A Systems Approach
ISBN: 0321278542
EAN: 2147483647
Year: 2006
Pages: 161
Authors: Amit Singh

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net