Fundamentals 28 min read

Understanding Mach Tasks, Threads, Processes and Exception Handling on iOS/macOS

The article explains how iOS/macOS uses the XNU kernel's Mach micro‑kernel to represent processes, tasks and threads, describes the relationship between them, classifies hardware and software exceptions, shows how Mach exceptions are translated into Unix signals, and provides practical code examples for capturing both Mach exceptions and signals in a crash‑reporting framework.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Understanding Mach Tasks, Threads, Processes and Exception Handling on iOS/macOS

Task & Thread & Process

iOS runs on the XNU kernel, which combines a Mach micro‑kernel with BSD services. Each running app instance is a process identified by a PID. In the BSD layer a process contains one or more threads and a corresponding Mach task , which is the kernel’s basic resource container.

Process

A process is the user‑level abstraction of an executable instance. It can join a process group via setpgrp(2) . In BSD, a process also includes internal threads and a Mach task.

Task

A Mach task holds a virtual address space, a port name space and one or more threads. It is similar to a process but provides fewer features (no signals, file descriptors, etc.). The BSD process maps one‑to‑one to a Mach task, which acts only as a resource container.

Thread

Threads are the basic CPU scheduling units. iOS threads are POSIX pthread objects built on top of Mach threads. A Mach thread belongs to a single task and shares the task’s address space.

Questions

1. Task vs. Process : each process has a corresponding Mach task that only contains resources. 2. Process vs. Thread : a thread is the smallest scheduling unit, while a process (or task) is the smallest resource‑allocation unit.

Exception Overview

When an application triggers a hardware exception, execution switches from user mode to kernel mode. The kernel converts the exception into a Mach exception message and sends it through a hierarchy of exception ports: thread → task → host.

Mach Exception Transmission

void exception_trige(exception_type_t exception, mach_excpetion_data_t code, mach_msg_type_number_t codeCnt) {
    // thread level
    thread = current_thread();
    excp = &thread->exc_actions[exception];
    kr = exception_deliver(thread, exception, code, codeCnt, excp, mutex);
    if (kr == KERN_SUCCESS) goto out;
    // task level
    task = current_task();
    excp = &task->exc_actions[exception];
    kr = exception_deliver(thread, exception, code, codeCnt, excp, mutex);
    if (kr == KERN_SUCCESS) goto out;
    // host level
    host_priv = host_priv_self();
    excp = &host_priv->exc_actions[exception];
    kr = exception_deliver(thread, exception, code, codeCnt, excp, mutex);
    if (kr == KERN_SUCCESS) goto out;
    // if none handled, terminate task
    (void)task_terminate(task);
out:
    if ((exception != EXC_CRASH) && (exception != EXC_RESOURCE))
        thread_exception_return();
    return;
}

If no handler returns KERN_SUCCESS , the kernel terminates the task.

Mach Exception → Unix Signal Mapping

Mach Exception

Unix Signal

Reason

EXC_BAD_INSTRUCTION

SIGILL

Illegal instruction

EXC_BAD_ACCESS

SIGSEGV / SIGBUS

Invalid memory access

EXC_ARITHMETIC

SIGFPE

Arithmetic error (e.g., divide‑by‑zero)

EXC_EMULATION

SIGEMT

Hardware emulation instruction

EXC_BREAKPOINT

SIGTRAP

Breakpoint / trace

EXC_SOFTWARE

SIGABRT, SIGPIPE, SIGSYS, SIGKILL

Software‑generated exceptions

Software exceptions (e.g., Objective‑C runtime errors) are turned directly into signals without passing through Mach.

Exception Handling Strategies

Two main ways to capture crashes:

Unix Signal handlers (catch all signals such as SIGABRT, SIGSEGV, etc.).

Mach Exception ports combined with Unix signals for hardware exceptions.

Signal‑Based Handler Example

static void ryRegisterSignal(int signal) {
    struct sigaction action;
    action.sa_sigaction = rySignalHandler;
    action.sa_flags = SA_NODEFER | SA_SIGINFO;
    sigemptyset(&action.sa_mask);
    sigaction(signal, &action, 0);
}

static void rySignalHandler(int signal, siginfo_t *info, void *context) {
    NSMutableString *mstr = [[NSMutableString alloc] init];
    [mstr appendString:@"Signal Exception:\n"];
    [mstr appendString:[NSString stringWithFormat:@"Signal %@ was raised.\n", signalName(signal)]];
    // write stack trace, thread info, etc.
    [mstr writeToFile:@"/Library/signal.txt" atomically:true encoding:NSUTF8StringEncoding error:nil];
    exit(-1);
}

After handling, the process exits to avoid a repeat loop.

Mach Exception Handler Skeleton

void setupMachHandler() {
    kern_return_t rc;
    rc = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &myexceptionPort);
    rc = mach_port_insert_right(mach_task_self(), myexceptionPort, myexceptionPort, MACH_MSG_TYPE_MAKE_SEND);
    exception_mask_t mask = EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION |
                           EXC_MASK_ARITHMETIC | EXC_MASK_SOFTWARE |
                           EXC_MASK_BREAKPOINT | EXC_MASK_CRASH;
    rc = task_set_exception_ports(mach_task_self(), mask, myexceptionPort,
                                  EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,
                                  THREAD_STATE_NONE);
    pthread_create(&thread, NULL, exc_handler, NULL);
}

static void *exc_handler(void *ignored) {
    Request exc;
    exc.Head.msgh_size = 1024;
    exc.Head.msgh_local_port = myexceptionPort;
    while (true) {
        mach_msg_return_t rc = mach_msg(&exc.Head, MACH_RCV_MSG|MACH_RCV_LARGE,
                                        0, exc.Head.msgh_size,
                                        exc.Head.msgh_local_port,
                                        MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
        if (rc != MACH_MSG_SUCCESS) break;
        // process exception, write crash file, etc.
        break; // for demo
    }
    exit(-1);
    return NULL;
}

If the handler does not exit, the offending thread remains blocked while the rest of the app continues to run.

Key Takeaways

Mach exceptions are the kernel’s low‑level way of reporting hardware faults; they are later translated to Unix signals for compatibility.

Software‑generated crashes (e.g., Objective‑C exceptions) bypass Mach and go straight to signals.

To capture all crashes you need both a Mach‑exception port (for hardware faults) and signal handlers (for software faults such as SIGABRT).

After handling, either re‑raise the signal with raise(signal) or terminate the process with exit(-1) to avoid infinite loops.

iOSException HandlingsignalCrash Reportingmach
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.