Section 9.7. Mach Exceptions


9.7. Mach Exceptions

Exceptions are synchronous interruptions to the normal flow of program control caused by the program itself. The following are examples of reasons why exceptions can occur:

  • Attempting to access nonexistent memory

  • Attempting to access memory that violates address-space protection

  • Failing to execute an instruction because of an illegal or undefined opcode or operand

  • Producing an arithmetic error such as division by zero, overflow, or underflow

  • Executing an instruction intended to support emulation

  • Hitting a debugger-installed breakpoint or other exceptions related to debugging, tracing, and error detection

  • Executing the system call instruction

Some exceptions do not represent abnormal conditions in that they are part of the normal functioning of the operating system. Examples of such exceptions include page faults and system calls. As we saw in Chapters 3 and 6, a Mac OS X program invokes a system call through the sc instruction, which causes a hardware exception. A system call exception is handled differently from other types by the operating system. The operating system also handles page faults transparently to user programs.

Several other types of exceptions must either be reported to user programs or otherwise require explicit handling. These include exceptions that may be deliberately caused by programs, such as by a debugger using hardware breakpoint and trace facilities.

The deliberate use of exceptions can be classified into categories such as error handling, debugging, and emulation/virtualization.


Mach provides an IPC-based exception-handling facility wherein exceptions are converted to messages. When an exception occurs, a message containing information about the exceptionsuch as the exception type, the thread that caused it, and the thread's containing taskis sent to an exception port. The reply to this message, which the thread waits for, indicates whether the exception was successfully handled by an exception handler. Exceptions are system-level primitives in Mach.

Sets of exception portsone port per exception typeare maintained at the host, task, and thread levels (Figure 933). When an exception message is to be delivered, the kernel attempts to deliver it to the most specific port first. If either the delivery or the processing of that message fails, the kernel attempts the next most specific port. Thus, the order is thread, task, and host. Typically, the delivery of an exception message fails at a given level because there is no exception handler registered at that level. Similarly, processing of the message fails because the handler returned an error.

Figure 933. Exception ports at the host, task, and thread levels

// osfmk/kern/exception.h struct exception_action {     struct ipc_port       *port;     // exception port     thread_state_flavor_t  flavor;     // state flavor to send     exception_behavior_t   behavior; // exception type to raise }; // osfmk/kern/host.h struct host {     ...     struct exception_action exc_actions[EXC_TYPES_COUNT];     ... }; // osfmk/kern/task.h struct task {     ...     struct exception_action exc_actions[EXC_TYPES_COUNT];     ... }; // osfmk/kern/thread.h struct thread {     ...     struct exception_action exc_actions[EXC_TYPES_COUNT];     ... };

As we saw earlier, by default, the thread-level exception ports are all set to the null port, and the task-level exception ports are inherited during fork(). Figure 934 shows the initialization of exception handling during bootstrap. In particular, the Unix exception handler is also initialized here. This handler translates several types of Mach exceptions to Unix signals. We discuss this mechanism in Section 9.8.8.

Figure 934. Initialization of exception handling during kernel bootstrap

// bsd/kern/bsd_init.c void bsdinit_task(void) {     struct proc    *p = current_proc();     struct uthread *ut;     kern_return_t   kr;     thread_act_t    th_act;     ...     // initialize the Unix exception handler     ux_handler_init();     th_act = current_thread();     // the various exception masks are defined in osfmk/mach/exception_types.h     (void)host_set_exception_ports(host_priv_self(),                               EXC_MASK_ALL & ~(EXC_MASK_SYSCALL |                               EXC_MASK_MACH_SYSCALL | EXC_MASK_RPC_ALERT),                               ux_exception_port, EXCEPTION_DEFAULT, 0);     (void)task_set_exception_ports(get_threadtask(th_act),                                EXC_MASK_ALL & ~(EXC_MASK_SYSCALL |                                EXC_MASK_MACH_SYSCALL | EXC_MASK_RPC_ALERT),                                ux_exception_port, EXCEPTION_DEFAULT, 0);     ...     // initiate loading of launchd }

Note that one or more exception ports at any level can be retrieved or set through <level>_get_exception_ports() and <level>_set_exception_ports(), respectively, where <level> is one of host, task, or thread.


9.7.1. Programmer-Visible Aspects of Mach's Exception-Handling Facility

A Mach exception handler is a recipient of exception messages. It runs in its own thread. Although it could be in the same task as the excepting thread, it often is in another task, such as a debugger. A more appropriate name for an excepting threadthe one in which the exception occursis the victim thread. The thread running the exception handler is called the handler thread. A thread attains handler status for a task or a thread by acquiring receive rights to an exception port of that task or thread. For example, if a thread wants to be the exception handler for a task, it can call task_set_exception_ports() to register one of its ports as one of the task's exception ports. A single port can be used to receive multiple types of exception messages, depending on the arguments to <level>_set_exception_ports().

kern_return_t task_set_exception_ports(task_t                task,                          exception_mask_t      exception_types,                          mach_port_t           exception_port,                          exception_behavior_t  behavior,                          thread_state_flavor_t flavor); kern_return_t task_get_exception_ports(task_t                     task,                          exception_mask_t           exception_types,                          exception_mask_array_t     old_masks,                          exception_handler_array_t  old_handlers,                          exception_behavior_array_t old_behaviors,                          exception_flavor_array_t   old_flavors);


Let us look at the parameters of task_set_exception_ports() first. exception_types is the bitwise OR of the exception type bits for which the port is being set. Table 93 shows the machine-independent exception types defined on Mac OS X. An exception message contains additional, machine-dependent information corresponding to the machine-independent exception type. For example, if an EXC_BAD_ACCESS exception occurs because of unaligned access on the PowerPC, machine-dependent information will include an exception code of EXC_PPC_UNALIGNED and an exception subcode whose value will be the contents of the Data Access Register (DAR). Table 96 in Section 9.7.2 shows the codes and subcodes corresponding to several Mach exceptions. The machine-dependent codes are defined in osfmk/mach/ppc/exception.h and osfmk/mach/i386/exception.h.

Table 93. Machine-Independent Mach Exceptions

Exception

Notes

EXC_BAD_ACCESS

Could not access memory

EXC_BAD_INSTRUCTION

Illegal or undefined instruction or operand

EXC_ARITHMETIC

Arithmetic exception (such as a division by zero)

EXC_EMULATION

Emulation support instruction encountered

EXC_SOFTWARE

Software-generated exception (such as a floating-point assist)

EXC_BREAKPOINT

Trace or breakpoint

EXC_SYSCALL

Unix system call

EXC_MACH_SYSCALL

Mach system call

EXC_RPC_ALERT

RPC alert (actually used during performance monitoring, not for RPC)


The behavior argument to task_set_exception_ports() specifies the type of exception message that should be sent when the exception occurs. Table 94 shows the machine-independent exception behaviors defined on Mac OS X.

Table 94. Machine-Independent Mach Exception Behaviors

Behavior

Notes

EXCEPTION_DEFAULT

Send a catch_exception_raise message including the thread identity.

EXCEPTION_STATE

Send a catch_exception_raise_state message including the thread state.

EXCEPTION_STATE_IDENTITY

Send a catch_exception_raise_state_identity message including the thread identity and state.


The flavor argument specifies the type of thread state to be sent with the exception message. Table 95 shows the machine-dependent[12] (PowerPC) thread state types on Mac OS X. If no thread state is desired along with the exception message, the flavor THREAD_STATE_NONE can be used. Note that regardless of whether thread state is sent in an exception message, the exception handler can use thread_get_state() and thread_set_state() to retrieve and set, respectively, the victim thread's machine-dependent state.

[12] The machine-dependent thread states for PowerPC and x86 are defined in osfmk/mach/ppc/thread_status.h and osfmk/mach/i386/thread_status.h, respectively.

Table 95. Machine-Dependent (PowerPC) Mach Thread States

Type

Notes

PPC_THREAD_STATE

Contains 32-bit GPRs, CR, CTR, LR, SRR0, SRR1, VRSAVE, and XER

PPC_FLOAT_STATE

Contains FPRs and FPSCR

PPC_EXCEPTION_STATE

Contains DAR, DSISR, and a value specifying the PowerPC exception that was taken

PPC_VECTOR_STATE

Contains VRs, VSCR, and a validity bitmap indicating the VRs that have been saved

PPC_THREAD_STATE64

Is the 64-bit version of PPC_THREAD_STATE

PPC_EXCEPTION_STATE64

Is the 64-bit version of PPC_EXCEPTION_STATE


Let us see what happens when an exception occurs in a thread. The kernel suspends the victim thread and sends an IPC message to the appropriate exception port. The victim remains suspended in the kernel until a reply is received. A thread within any task with receive rights to the exception port may retrieve the message. Such a threadthe exception handler for that messagecalls exc_server() to handle the message. exc_server() is a MIG-generated server-handling function available in the system library. It performs the necessary argument handling for the kernel message, decodes the message, and calls one of the following programmer-provided functions: catch_exception_raise(), catch_exception_raise_identity(), or catch_exception_raise_state_identity(). As shown in Table 94, the behavior specified when the exception port was registered determines which of these functions will be called by exc_server(). All three functions are meant to handle the exception and return a value that determines what the kernel does next with the victim thread. In particular, if a catch_exception_raise function returns KERN_SUCCESS, exc_server() prepares a return message to be sent to the kernel that causes the thread to continue execution from the point of the exception. For example, if the exception was not fatal and the catch_exception_raise function fixed the problemperhaps by modifying the thread's stateit may be desirable for the thread to continue. A catch_exception_raise function may use a variety of thread functions to affect the course of actions, for example, thread_abort(), thread_suspend(), thread_resume(), thread_set_state(), and so on. If KERN_SUCCESS is not returned, the kernel will send the exception message to the next-level exception handler.

boolean_t exc_server(mach_msg_header_t request_msg, mach_msg_header_t reply_msg); kern_return_t catch_exception_raise(mach_port_t            exception_port,                       mach_port_t            thread,                       mach_port_t            task,                       exception_type_t       exception,                       exception_data_t       code,                       mach_msg_type_number_t code_count); // osfmk/mach/exception_types.h typedef integer_t *exception_data_t; // osfmk/mach/exc.defs type exception_data_t           = array[*:2] of integer_t; type exception_type_t           = int;


thread_set_state() allows the state of the victim thread to be crafted as desired. In particular, the resumption point of the thread can be modified.


9.7.2. The Mach Exception-Handling Chain

As we saw in Chapter 6, the low-level trap handler calls trap() [osfmk/ppc/trap.c] to perform higher-level trap processing, passing it the trap number, the saved state, and the contents of the DSISR and DAR registers (if applicable). TRap() deals with several types of exceptions: preemptions, page faults, performance-monitoring exceptions, software-generated ASTs, and so on. Exceptions that are known to the Mach exception-handling facility are passed up to Mach by calling doexception() [osfmk/ppc/trap.c]. Table 96 shows how low-level traps are translated to Mach exception data. doexception() calls exception_triage() [osfmk/kern/exception.c], which attempts to make an upcall to the thread's exception server. Figure 935 shows the important kernel functions involved in exception delivery.

Table 96. Traps and Corresponding Mach Exception Data

Trap Identifier

Mach Exception

Exception Code

Exception Subcode

T_ALTIVEC_ASSIST

EXC_ARITHMETIC

EXC_PPC_ALTIVECASSIST

Saved SRR0

T_DATA_ACCESS

EXC_BAD_ACCESS

Computed

DAR

T_INSTRUCTION_ACCESS

EXC_BAD_ACCESS

Computed

Saved SRR0

T_INSTRUCTION_BKPT

EXC_BREAKPOINT

EXC_PPC_TRACE

Saved SRR0

T_PROGRAM

EXC_ARITHMETIC

EXC_ARITHMETIC

Saved FPSCR

T_PROGRAM

EXC_BAD_INSTRUCTION

EXC_PPC_UNIPL_INST

Saved SRR0

T_PROGRAM

EXC_BAD_INSTRUCTION

EXC_PPC_PRIVINST

Saved SRR0

T_PROGRAM

EXC_BREAKPOINT

EXC_PPC_BREAKPOINT

Saved SRR0

T_PROGRAM

EXC_SOFTWARE

EXC_PPC_TRAP

Saved SRR0


Figure 935. Kernel functions involved in Mach exception delivery


// osfmk/ppc/trap.c void doexception(int exc, int code, int sub) {     exception_data_type_t codes[EXCEPTION_CODE_MAX];     codes[0] = code;     codes[1] = sub;     exception_triage(exc, codes, 2); }


9.7.2.1. Delivering Exceptions

exception_triage() is so called because it first attempts to raise the exception at the thread level, failing which it attempts the task and host levels, in that order. Raising an exception involves calling exception_deliver() [osfmk/kern/exception.c], which calls one of the following MIG routines, depending on the exception behavior: exception_raise(), exception_raise_state(), or exception_raise_state_identity(). The exception is caught by the handler calling one of the catch_exception_raise functions we saw in Section 9.7.1.

If the exception remains unhandled at all levels, exception_triage() attempts to call the built-in kernel debugger if it is available. If all these attempts fail, the task is terminated. Figure 936 shows an excerpt from the relevant code in the kernel.

Figure 936. Delivery of Mach exceptions

// osfmk/kern/exception.c // Current thread caught an exception; make an upcall to the exception server void exception_triage(exception_type_t       exception,                  exception_data_t       code,                  mach_msg_type_number_t codeCnt) {     ...     // Try to raise the exception at the thread level     thread = current_thread();     mutex  = mutex_addr(thread->mutex);     excp   = &thread->exc_actions[exception];     exception_deliver(exception, code, codeCnt, excp, mutex);     // We're still here, so delivery must have failed     // Try to raise the exception at the task level     task  = current_task();     mutex = mutex_addr(task->lock);     excp  = &task->exc_actions[exception];     exception_deliver(exception, code, codeCnt, excp, mutex);     // Still failed; try at the host level     host_priv = host_priv_self();     mutex     = mutex_addr(host_priv->lock);     excp      = &host_priv->exc_actions[exception];     exception_deliver(exception, code, codeCnt, excp, mutex); #if MACH_KDB     // If KDB is enabled, debug the exception with KDB #endif     // All failed; terminate the task     ... }

9.7.2.2. Unresolved Kernel Traps

If there is an exception that neither maps to a Mach exception nor can be dealt with otherwise, it leads to an unresolved kernel trap. For example, if TRap() encounters an unexpected trap numbersay, one that should have been handled earlier in the overall exception-handling chain, or one that is fatal in the kernelit calls unresolved_kernel_trap() [osfmk/ppc/trap.c], which dumps debugging information on the screen and then either calls the debugger or panics the system (Figure 937).

Figure 937. Processing of unresolved kernel traps

// osfmk/ppc/trap.c void unresolved_kernel_trap(int              trapno,                        struct savearea *ssp,                        unsigned int     dsisr,                        addr64_t         dar,                        char            *message) {     ...     kdb_printf("\n\nUnresolved kernel trap(cpu %d): %s DAR=0x%016llX PC=%016llX\n",                cpu_number(), trap_name, dar, ssp->save_ssr0);     // this comes from osfmk/ppc/model_dep.c     print_backtrace(ssp);     ...     draw_panic_dialog();     if (panicDebugging)         (void *)Call_Debugger(trapno, ssp);     panic(message); } // osfmk/console/panic_dialog.c void draw_panic_dialog(void) {     ...     if (!panicDialogDrawn && panicDialogDesired) {         if (!logPanicDataToScreen) {             ...             // dim the screen 50% before putting up the panic dialog             dim_screen();             // set up to draw background box and draw panic dialog             ...             panic_blit_rect(...);             // display the MAC address and the IP address, but only if the             // machine is attachable, to avoid end-user confusion             if (panicDebugging) {                 ...                 // blit the digits for MAC address and IP address                 ...             }     }     panicDialogDrawn = TRUE;     panicDialogDesired = FALSE; }

9.7.3. Example: A Mach Exception Handler

Let us see a programming example to understand the working of a Mach exception handler. In our program, we will allocate a Mach port and set it as the exception port of the program's main thread. We will be interested only in illegal instruction exceptions, so we will specify EXC_MASK_BAD_INSTRUCTION as the exception mask value when calling thread_set_exception_ports(). Moreover, we will ask for the default exception behavior, with no exception state, to be sent along with the message.

Then, we will create another thread to run the exception handler. This second thread will receive exception messages on the main thread's exception port. Once a message arrives, it will call exc_server(). We will then deliberately cause an exception to occur by trying to execute noninstruction data. Since we asked for the default behavior, exc_server() will call catch_exception_raise(). In our implementation of catch_exception_raise(), we will call tHRead_get_state() to retrieve the victim thread's machine state. We will modify the SRR0 value to contain the address of a function that will simply print a message, thereby causing the victim thread to die gracefully. We will use tHRead_set_state() to set the modified state, after which we will return KERN_SUCCESS from catch_exception_raise(). When the consequent reply is sent to the kernel, it will continue the thread.

Perhaps the most critical piece of information we need to write an exception handler is the format of the exception message that will be sent to us by the kernel. The implementation of ux_handler() [bsd/uxkern/ux_exception.c] provides this information. We call our exception message's data type exc_msg_t. Note that we use a large trailing pad. The NDR field contains a Network Data Representation (NDR) record [osfmk/mach/ndr.h] that we will not deal with.

Figure 938 shows the program. It can be trivially ported to the x86 version of Mac OS X.

Figure 938. An exception handler for "fixing" illegal instructions

// exception.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <mach/mach.h> // exception message we will receive from the kernel typedef struct exc_msg {     mach_msg_header_t          Head;     mach_msg_body_t            msgh_body; // start of kernel-processed data     mach_msg_port_descriptor_t thread;    // victim thread     mach_msg_port_descriptor_t task;      // end of kernel-processed data     NDR_record_t               NDR;       // see osfmk/mach/ndr.h     exception_type_t           exception;     mach_msg_type_number_t     codeCnt;   // number of elements in code[]     exception_data_t           code;      // an array of integer_t     char                       pad[512];  // for avoiding MACH_MSG_RCV_TOO_LARGE } exc_msg_t; // reply message we will send to the kernel typedef struct rep_msg {     mach_msg_header_t          Head;     NDR_record_t               NDR;       // see osfmk/mach/ndr.h     kern_return_t              RetCode;   // indicates to the kernel what to do } reply_msg_t; // exception handling mach_port_t exception_port; void exception_handler(void); extern boolean_t exc_server(mach_msg_header_t *request,                             mach_msg_header_t *reply); // demonstration function and associates typedef void(* funcptr_t)(void); funcptr_t     function_with_bad_instruction; kern_return_t repair_instruction(mach_port_t victim); void          graceful_dead(void); // support macros for pretty printing #define L_MARGIN "%-21s: " #define FuncPutsN(msg)   printf(L_MARGIN "%s", __FUNCTION__, msg) #define FuncPuts(msg)    printf(L_MARGIN "%s\n", __FUNCTION__, msg) #define FuncPutsIDs(msg) printf(L_MARGIN "%s (task %#lx, thread %#lx)\n", \                                 __FUNCTION__, msg, (long)mach_task_self(), \                                 (long)pthread_mach_thread_np(pthread_self())); #define EXIT_ON_MACH_ERROR(msg, retval) \     if (kr != KERN_SUCCESS) { mach_error(msg ":" , kr); exit((retval)); } #define OUT_ON_MACH_ERROR(msg, retval) \     if (kr != KERN_SUCCESS) { mach_error(msg ":" , kr); goto out; } int main(int argc, char **argv) {     kern_return_t kr;     pthread_t     exception_thread;     mach_port_t   mytask = mach_task_self();     mach_port_t   mythread = mach_thread_self();     FuncPutsIDs("starting up");     // create a receive right     kr = mach_port_allocate(mytask, MACH_PORT_RIGHT_RECEIVE, &exception_port);     EXIT_ON_MACH_ERROR("mach_port_allocate", kr);     // insert a send right: we will now have combined receive/send rights     kr = mach_port_insert_right(mytask, exception_port, exception_port,                                 MACH_MSG_TYPE_MAKE_SEND);     OUT_ON_MACH_ERROR("mach_port_insert_right", kr);     kr = thread_set_exception_ports(mythread,                 // target thread                                     EXC_MASK_BAD_INSTRUCTION, // exception types                                     exception_port,           // the port                                     EXCEPTION_DEFAULT,        // behavior                                     THREAD_STATE_NONE);       // flavor     OUT_ON_MACH_ERROR("thread_set_exception_ports", kr);     if ((pthread_create(&exception_thread, (pthread_attr_t *)0,                         (void *(*)(void *))exception_handler, (void *)0))) {         perror("pthread_create");         goto out;     }     FuncPuts("about to dispatch exception_handler pthread");     pthread_detach(exception_thread);     // some random bad address for code, but otherwise a valid address     function_with_bad_instruction = (funcptr_t)exception_thread;     FuncPuts("about to call function_with_bad_instruction");     function_with_bad_instruction();     FuncPuts("after function_with_bad_instruction"); out:     mach_port_deallocate(mytask, mythread);     if (exception_port)         mach_port_deallocate(mytask, exception_port);     return 0; } void exception_handler(void) {     kern_return_t kr;     exc_msg_t     msg_recv;     reply_msg_t   msg_resp;     FuncPutsIDs("beginning");     msg_recv.Head.msgh_local_port = exception_port;     msg_recv.Head.msgh_size = sizeof(msg_recv);     kr = mach_msg(&(msg_recv.Head),            // message                   MACH_RCV_MSG|MACH_RCV_LARGE, // options                   0,                           // send size (irrelevant here)                   sizeof(msg_recv),            // receive limit                   exception_port,              // port for receiving                   MACH_MSG_TIMEOUT_NONE,       // no timeout                   MACH_PORT_NULL);             // notify port (irrelevant here)     EXIT_ON_MACH_ERROR("mach_msg_receive", kr);     FuncPuts("received message");     FuncPutsN("victim thread is ");     printf("%#lx\n", (long)msg_recv.thread.name);     FuncPutsN("victim thread's task is ");     printf("%#lx\n", (long)msg_recv.task.name);     FuncPuts("calling exc_server");     exc_server(&msg_recv.Head, &msg_resp.Head);     // now msg_resp.RetCode contains return value of catch_exception_raise()     FuncPuts("sending reply");     kr = mach_msg(&(msg_resp.Head),        // message                   MACH_SEND_MSG,           // options                   msg_resp.Head.msgh_size, // send size                   0,                       // receive limit (irrelevant here)                   MACH_PORT_NULL,          // port for receiving (none)                   MACH_MSG_TIMEOUT_NONE,   // no timeout                   MACH_PORT_NULL);         // notify port (we don't want one)     EXIT_ON_MACH_ERROR("mach_msg_send", kr);     pthread_exit((void *)0); } kern_return_t catch_exception_raise(mach_port_t            port,                       mach_port_t            victim,                       mach_port_t            task,                       exception_type_t       exception,                       exception_data_t       code,                       mach_msg_type_number_t code_count) {     FuncPutsIDs("beginning");     if (exception != EXC_BAD_INSTRUCTION) {         // this should not happen, but we should forward an exception that we         // were not expecting... here, we simply bail out         exit(-1);     }     return repair_instruction(victim); } kern_return_t repair_instruction(mach_port_t victim) {     kern_return_t      kr;     unsigned int       count;     ppc_thread_state_t state;     FuncPutsIDs("fixing instruction");     count = MACHINE_THREAD_STATE_COUNT;     kr = thread_get_state(victim,                 // target thread                           MACHINE_THREAD_STATE,   // flavor of state to get                           (thread_state_t)&state, // state information                           &count);                // in/out size     EXIT_ON_MACH_ERROR("thread_get_state", kr);     // SRR0 is used to save the address of the instruction at which execution     // continues when rfid executes at the end of an exception handler routine     state.srr0 = (vm_address_t)graceful_dead;     kr = thread_set_state(victim,                      // target thread                           MACHINE_THREAD_STATE,        // flavor of state to set                           (thread_state_t)&state,      // state information                           MACHINE_THREAD_STATE_COUNT); // in size     EXIT_ON_MACH_ERROR("thread_set_state", kr);     return KERN_SUCCESS; } void graceful_dead(void) {     FuncPutsIDs("dying graceful death"); } $ gcc -Wall -o exception exception.c $ ./exception main                 : starting up (task 0x807, thread 0xd03) main                 : about to dispatch exception_handler pthread main                 : about to call function_with_bad_instruction exception_handler    : beginning (task 0x807, thread 0xf03) exception_handler    : received message exception_handler    : victim thread is 0xd03 exception_handler    : victim thread's task is 0x807 exception_handler    : calling exc_server catch_exception_raise: beginning (task 0x807, thread 0xf03) repair_instruction   : fixing instruction (task 0x807, thread 0xf03) exception_handler    : sending reply graceful_dead        : dying graceful death (task 0x807, thread 0xd03) main                 : after function_with_bad_instruction

The use of exc_server() in Figure 938 is a typical example of Mach server programming. Other such server functions can be used to replace repetitive code for receiving and sending messages. For example, mach_msg_server() is a generic server function whose arguments include a port (receive rights) and a pointer to a message demultiplexing function. It runs the following loop internally: receive a request message, call the demultiplexer with request and reply buffers, and possibly send a reply message based on the demultiplexer's return value.

mach_msg_return_t mach_msg_server(boolean_t          (*demux)(mach_msg_header_t *,                                             mach_msg_header_t *),                 mach_msg_size_t    max_size,                 mach_port_t        rcv_name,                 mach_msg_options_t options);


mach_msg_server_once() is a variant that processes only one request and then returns to the user. In fact, we can replace the entire implementation of exception_handler() in Figure 938 with the following code, using exc_server() as the demultiplexing function.

void exception_handler(void) {     (void)mach_msg_server_once(exc_server,             // demultiplexing function                                sizeof(exc_msg_t),      // maximum receive size                                exception_port,         // port for receiving                                MACH_MSG_TIMEOUT_NONE); // options, if any     pthread_exit((void *)0); }





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