$30 off During Our Annual Pro Sale. View Details »

IPC on macOS

IPC on macOS

vashchenko

August 17, 2019
Tweet

More Decks by vashchenko

Other Decks in Programming

Transcript

  1. IPC on macOS

    View Slide

  2. Before we dive in
    The Berkeley Software Distribution (BSD) is an operating
    system based on early Unix.

    View Slide

  3. BSD became the basics for several open-source OS:
    FreeBSD, OpenBSD, NetBSD, DragonFly BSD, TrueOS and
    Darwin

    View Slide

  4. Darwin is an open-source Unix-like operating system
    first released by Apple in 2000

    View Slide

  5. Darwin forms the core set of components upon which
    macOS, iOS, watchOS, tvOS, iPadOS and audioOS are
    based

    View Slide

  6. View Slide

  7. BSD supports POSIX standard, so POSIX APIs are
    avaliable to us on macOS.

    View Slide

  8. POSIX == Portable Operating System Interface

    View Slide

  9. Available IPC techniques

    View Slide

  10. BSD
    1. BSD sockets
    2. BSD pipes
    3. BSD signals

    View Slide

  11. Other low level
    1. Shared Memory + Semaphores for large resources
    (like movie)

    View Slide

  12. Darwin low-level
    1. Mach Ports

    View Slide

  13. The Mach port object is the underlying primitive used for
    all interprocess communication in macOS

    View Slide

  14. High-level macOS-specific
    1. Apple Events
    2. High-performance counterpart of Apple Events —
    CFMessagePort and friends
    3. Distributed Notifications
    4. Distributed objects (NSConnection and friends)
    5. XPC (C API and Objective-C API)

    View Slide

  15. Even higher level
    no explicit setup for communication
    1. NSPasteboard
    2. Services API

    View Slide

  16. BSD Sockets
    You can implement inter-process communication using
    BSD sockets via:
    — POSIX API (socket() and friends)
    — Core Foundation API (CFSocket and friends; also used
    for networking)

    View Slide

  17. BSD Pipes
    One-way communication. 2 types:
    — Unnamed pipe (example: command line shell cat
    magic.txt | grep -e Gwendoyln sends the contents of
    magic.text to the grep command)
    — Named pipe is a special type of file without any
    content on filesystem

    View Slide

  18. BSD signals
    Usually a process terminates on receiving a signal. But it
    can implement a signal handler function to override this
    behaviour.
    Used by OS kernel to tell the process about an exception
    (like division by 0). Command line kill command is
    another example of using signals.
    The signal namespace is just one integer, so collisions
    are very possible.
    Not recommended to use signals for IPC.

    View Slide

  19. Shared Memory + Semaphores
    A process allocates a memory region, that can be read
    and written by other processes.
    Advantages:
    * data is never copied
    * any process with appropriate permission can read and
    write
    Disadvantage:
    * fragile—if it is corrupted, all processes are corrupted
    Access it from one process, use more conventional IPC.

    View Slide

  20. Mach Ports
    Available APIs:
    — Darwin C API
    — Core Foundation — CFMachPort and friends
    — Foundation — NSMachPort and friends

    View Slide

  21. Darwin C API: sending
    natural_t data;
    mach_port_t port;
    struct {
    mach_msg_header_t header;
    mach_msg_body_t body;
    mach_msg_type_descriptor_t type;
    } message;
    message.header = (mach_msg_header_t) {
    .msgh_remote_port = port,
    .msgh_local_port = MACH_PORT_NULL,
    .msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0),
    .msgh_size = sizeof(message)
    };

    View Slide

  22. message.body = (mach_msg_body_t) {
    .msgh_descriptor_count = 1
    };
    message.type = (mach_msg_type_descriptor_t) {
    .pad1 = data,
    .pad2 = sizeof(data)
    };
    mach_msg_return_t error = mach_msg_send(&message.header);
    if (error == MACH_MSG_SUCCESS) {
    // ...
    }

    View Slide

  23. Darwin C API
    Not recommended to use directly: interfaces will change
    with kernel updates.

    View Slide

  24. CFMachPort & NSMachPort

    View Slide

  25. Apple Events

    View Slide

  26. Apple Events
    NSAppleEventManager *appleEventManager = [NSAppleEventManager sharedAppleEventManager];
    [appleEventManager setEventHandler:self
    andSelector:@selector(handleAppleEvent:withReplyEvent:)
    forEventClass:kInternetEventClass
    andEventID:kAEGetURL];

    View Slide

  27. View Slide

  28. CFMessagePort

    View Slide

  29. CFMessagePort
    High-performance counterpart of Apple Events

    View Slide

  30. private struct RemoteServerPortName {
    static let client = "com.hideez.port.client"
    static let server = "com.hideez.port.server"
    }
    private var inPort: CFMessagePort?
    func startListening() {
    guard nil == inPort else {
    return
    }
    inPort = CFMessagePortCreateLocal(
    nil, RemoteServerPortName.client as CFString!, getInMessageCallback(), nil, nil)
    let runLoopSource = CFMessagePortCreateRunLoopSource(nil, inPort, 0);
    CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, CFRunLoopMode.commonModes);
    }
    func getInMessageCallback() -> CFMessagePortCallBack {
    return {(port: CFMessagePort?, messageId:Int32, data: CFData?,
    _: UnsafeMutableRawPointer?) -> Unmanaged? in
    RemoteServer.shared.handleInMessage(port: port, messageId: messageId, data: data)
    }
    }

    View Slide

  31. func postMessage(message: RemoteMessageOut,
    waitForResponse wait: Bool = false) -> RemoteServerResponse? {
    guard let outPort = CFMessagePortCreateRemote(
    nil, RemoteServerPortName.server as CFString!) else {
    return nil
    }
    var respData: Unmanaged? = Unmanaged.passRetained(CFDataCreate(nil, nil, 0)!)
    CFMessagePortSendRequest(
    outPort,
    message.id.rawValue,
    (message.data ?? Data()) as CFData,
    30.0,
    wait ? 30.0 : 0.0,
    wait ? CFRunLoopMode.defaultMode.rawValue : nil,
    &respData)
    let data = respData!.takeUnretainedValue() as Data
    CFMessagePortInvalidate(outPort)
    return data.isEmpty ? nil : NSKeyedUnarchiver.unarchiveObject(with: data) as? [String : Any]
    }

    View Slide

  32. Distributed Notifications

    View Slide

  33. [NSDistributedNotificationCenter default addObserver:self
    selector:@selector(_openPreferences)
    name:notificationName
    object:nil];

    View Slide

  34. distributedNotificationCenter.postNotificationName(@"NotificationName",
    object: nil,
    userInfo: userInfo,
    deliverImmediately: true)

    View Slide

  35. Distributed objects
    Deprecated and has
    !!!

    View Slide

  36. Distributed objects
    NSConnection and friends
    are deprecated and have
    !!!

    View Slide

  37. XPC
    Available APIs:
    — C API (use from C, C++ code)
    — Objective-C API (use from Objective-C, Swift)

    View Slide

  38. Obj-C XPC — modern API
    — Protocol-oriented(!)
    — Available since 10.8 (year of 2012)
    — API of choice for IPC on macOS

    View Slide

  39. View Slide

  40. View Slide

  41. View Slide

  42. IPC workshop

    View Slide

  43. Thank you!

    View Slide