進程間通信之XPC

From:https://blog.csdn.net/heikefangxian23/article/details/51071840

關於 XPC

XPC 是 OS X 下的一種 IPC (進程間通信) 技術, 它實現了權限隔離, 使得 App Sandbox 更加完備.

首先,XPC 更多關注的是實現功能某種的方式,通常採用其他方式同樣能夠實現。並沒有強調如果不使用 XPC,無法實現某些功能。

XPC 目的是提高 App 的安全性和穩定性。XPC 讓進程間通信變得更容易,讓我們能夠相對容易地將 App 拆分成多個進程的模式。更進一步的是,XPC 幫我管理了這些進程的生命週期,當我們需要與子進程通信的時候,子進程已經被 XPC 給運行起來了。

我們將使用在頭文件 NSXPCConnection.h 中聲明的 Foundation framework API,它是建立在頭文件 xpc/xpc.h 中聲明的原始 XPC API 之上的。XPC API 原本是純 C 實現的 API,很好地集成了 libdispatch(又名 GCD)。本文中我們將使用Foundation 中的類,它們可以讓我們使用 XPC 的幾乎全部功能(真實的表現了實際底層 C API 是如何工作的),同時與 C API 相比,Foundation API 使用起來會更加容易。

哪些地方用到了 XPC ?

Apple 在操作系統的各個部分廣泛使用了 XPC,很多系統 Framework 也利用了 XPC 來實現其功能。你可以在命令行運行如下搜索命令:

% find /System/Library/Frameworks -name \*.xpc

結果顯示 Frameworks 目錄下有 55 個 XPC service(譯者注:在 Yosemite 下),範圍從 AddressBook 到 WebKit 等等。

如果在 /Applications 目錄下做同樣的搜索,我們會發現從 iWork 套件到 Xcode,甚至是一些第三方應用程序都使用了 XPC。

Xcode 本身就是使用 XPC 的一個很好的例子:當你在 Xcode 中編輯 Swift 代碼的時候,Xcode 就是通過 XPC 與 SourceKit 通信的(譯者注:實際進程名應該是SourceKitService)。SourceKit 是主要負責源代碼解析,語法高亮,排版,自動完成等功能的 XPC service。更多詳情可以參考 JP Simard 的博客.

其實 XPC 在 iOS 上應用的很廣泛 - 但是目前只有 Apple 能夠使用,第三方開發者還不能使用。

一個示例 App

讓我們來看一個簡單的示例:一個在 table view 中顯示多張圖片的 App。圖片是以 JPEG 格式從網絡服務器上下載下來的。

App看起來是這樣:

NSTableViewDataSource 會從 ImageSet 類加載圖片 ,像這樣:

func tableView(tableView: NSTableView!, viewForTableColumn tableColumn: NSTableColumn!, row: Int) -> NSView! {
    let cellView = tableView.makeViewWithIdentifier("Image", owner: self) as NSTableCellView
    var image: NSImage? = nil
    if let c = self.imageSet?.images.count {
        if row < c {
            image = self.imageSet?.images[row]
        }
    }
    cellView.imageView.image = image
    return cellView
}

ImageSet 類有一個簡單的屬性:

var images: NSImage![]

ImageLoader 類會異步的填充這個圖片數組。

不使用XPC

如果不使用XPC,我們可以這樣實現 ImageLoader 類來下載並解壓圖片:

class ImageLoader: NSObject {
    let session: NSURLSession

    init()  {
        let config = NSURLSessionConfiguration.defaultSessionConfiguration()
        session = NSURLSession(configuration: config)
    }

    func retrieveImage(atURL url: NSURL, completionHandler: (NSImage?)->Void) {
        let task = session.dataTaskWithURL(url) {
            maybeData, response, error in
            if let data: NSData = maybeData {
                dispatch_async(dispatch_get_global_queue(0, 0)) {
                    let source = CGImageSourceCreateWithData(data, nil).takeRetainedValue()
                    let cgImage = CGImageSourceCreateImageAtIndex(source, 0, nil).takeRetainedValue()
                    var size = CGSize(
                        width: CGFloat(CGImageGetWidth(cgImage)),
                        height: CGFloat(CGImageGetHeight(cgImage)))
                    let image = NSImage(CGImage: cgImage, size: size)
                    completionHandler(image)
                }
            }
        }
        task.resume()
    }
}

明確而且工作得很好。

錯誤隔離 (Fault Isolation) 和 權限隔離 (Split Privileges)

我們的 App 做了三件不同的事情:從互聯網上下載數據,解碼爲 JPEG,然後顯示。

如果把 App 拆分成三個獨立的進程,我們就能給每個進程單獨的權限了;UI 進程並不需要訪問網絡的權限。圖片下載的進程的確需要訪問網絡,但它不需要訪問文件的權限(它只是轉發數據,並不做保存)。而將 JPEG 圖片解碼爲 RGB 數據的進程既不需要訪問網絡的權限,也不需要訪問文件的權限。

通過這種方式,在我們的 App 中尋找安全漏洞的行爲已經變得很困難了。另一個好處是,我們的 App 會變得更穩定;例如下載 service 因爲 bug 導致的 crash 並不會影響 App 主進程的運行;而下載 service 會被重啓。

架構圖如下:

image

XPC 的使用十分靈活,我們還可以這樣設計:讓 App 直接和兩個 service 通信,由 App 來負責 service 之間的數據交互。後面我們會看到 App 是如何找到 XPC services的。

迄今爲止,大部分安全相關的 bug 都出現在解析不受信數據的過程當中,例如數據是我們從互聯網上接收到的,不受我們控制的。現實中 HTTP 協議和解析 JPEG 數據也需要處理這樣的問題,而通過這樣設計,我們將解析不受信數據的過程挪進了一個子進程,即一個 XPC service。

在 App 中使用 XPC Services

XPC service 由兩個部分組成:service 本身,以及與之通信的代碼。它們都很簡單而且相似,算是個好消息。

在 Xcode 中有模板可以添加新的 XPC service target。 每個 service 都需要一個 bundle id,一個好的實踐是將其設置爲 App 的 bundle id 的 subdomain(子域)。

在我們的例子中,App 的 bundle id 是 io.objc.Superfamous-Images,我們可以把下載 service 的 bundle id 設爲 io.objc.Superfamous-Images.ImageDownloader

在 build 過程中,Xcode 會爲 service target 創建一個獨立 bundle,這個 bundle 會被複制到 XPCServices目錄下,與 Resources 目錄平級。

當 App 將數據發給 io.objc.Superfamous-Images.ImageDownloader 這個 service 時,XPC 會自動啓動這個 service。

基於 XPC 的通信基本都是異步的。我們通過一個 App 和 service 都使用的 protocol 來進行定義。在我們的例子中:

@objc(ImageDownloaderProtocol) protocol ImageDownloaderProtocol {
    func downloadImage(atURL: NSURL!, withReply: (NSData?)->Void)
}

請注意 withReply: 這部分。它表明了消息是如何通過異步的方式回給調用方的。若返回的消息帶有數據,需要將函數簽名最後一部分寫成:withReply: 並接受一個閉包參數的形式。

在我們的例子中,service 只提供了一個方法;但是我們可以在 protocol 裏定義多個方法。

App 到 service 的連接是通過創建 NSXPCConnection 對象來完成的,像這樣:

let connection = NSXPCConnection(serviceName: "io.objc.Superfamous-Images.ImageDownloader")
connection.remoteObjectInterface = NSXPCInterface(`protocol`: ImageDownloaderProtocol.self)
connection.resume()

我們可以把 connection 對象保存爲 self.imageDownloadConnection,這樣之後就可以像這樣和 service 進行通信了:

let downloader = self.imageDownloadConnection.remoteObject as ImageDownloaderProtocol
downloader.downloadImageAtURL(url) {
    (data) in
    println("Got \(data.length) bytes.")
}

我們還應該給 connection 對象設置錯誤處理函數,像這樣:

let downloader = self.imageDownloadConnection.remoteObjectProxyWithErrorHandler {
        (error) in NSLog("remote proxy error: %@", error)
} as ImageDownloaderProtocol
downloader.downloadImageAtURL(url) {
    (data) in
    println("Got \(data.length) bytes.")
}

這就是 App 端的所有代碼了。

監聽service請求

XPC service 通過 NSXPCListener 對象來監聽從 App 傳入的請求(譯者注:這是 NSXPCListenerDelegate中可選的方法)。listener 對象會給每個來自 App 的請求在 service 端創建對應的 connection 對象。

在 main.swift 中,我們可以這樣寫:

class ServiceDelegate : NSObject, NSXPCListenerDelegate {
    func listener(listener: NSXPCListener!, shouldAcceptNewConnection newConnection: NSXPCConnection!) -> Bool {
        newConnection.exportedInterface = NSXPCInterface(`protocol`: ImageDownloaderProtocol.self)
        var exportedObject = ImageDownloader()
        newConnection.exportedObject = exportedObject
        newConnection.resume()
        return true
    }
}

// Create the listener and run it by resuming:
let delegate = ServiceDelegate()
let listener = NSXPCListener.serviceListener()
listener.delegate = delegate;
listener.resume()

我們創建了一個全局(相當於 C 或 Objective-C 中的 main 函數)的 NSXPCListener 對象,並設置了它的 delegate,這樣傳入連接就會調用我們的 delegate 方法了。我們需要給 connection 設置 App 端中同樣使用的 protocol。最後我們設置 ImageDownloader 實例,它實際上實現了接口:

class ImageDownloader : NSObject, ImageDownloaderProtocol {
    let session: NSURLSession

    init()  {
        let config = NSURLSessionConfiguration.defaultSessionConfiguration()
        session = NSURLSession(configuration: config)
    }

    func downloadImageAtURL(url: NSURL!, withReply: ((NSData!)->Void)!) {
        let task = session.dataTaskWithURL(url) {
            data, response, error in
            if let httpResponse = response as? NSHTTPURLResponse {
                switch (data, httpResponse) {
                case let (d, r) where (200 <= r.statusCode) && (r.statusCode <= 399):
                    withReply(d)
                default:
                    withReply(nil)
                }
            }
        }
        task.resume()
    }
}

值得注意的一個重要是,NSXPCListener 和 NSXPCConnection 默認是掛起 (suspended) 的。我們設置好後需要調用它們的 resume 方法來啓動。

GitHub上可以找到這個簡單的示例App。

監聽者 (Listener),連接 (Connection) 和導出對象 (Exported Object)

在 App 端,我們有一個 connection 對象。每次將數據發給 service 時,我們需要調用 remoteObjectProxyWithErrorHandler 方法來創建一個遠程對象代理 (remote object proxy)。

而在service端,則多了一層。首先需要一個 listener,用來監聽來自 App 的傳入 connection。App 可以創建多個 connection,listener 會在 service 端建立相應的 connection 對象。每個 connection 對象都有唯一的 exported object,在 App 端,通過 remote object proxy 發送的消息就是給它的。

當 App 創建一個到 XPC service 的 connection 時,是 XPC 在管理這個 service 的生命週期,service 的啓動與停止都由 XPC runtime 完成,這對 App 來說是透明的。而且如果 service 因爲某種原因 crash 了,也會透明地被重啓。

App 初始化 XPC connection 的時候,XPC service 並不會啓動,直到 App 實際發送的第一條消息到 remote object proxy 時才啓動。如果當前沒有未結束的響應,系統可能會因爲內存壓力或者 XPC service 已經閒置了一段時間而停止這個 service。這種情況下,App 持有的 connection 對象任然有效,下次再使用這個 connection 對象的時候,XPC 系統會自動重啓對應的 XPC service。

如果 XPC service crash 了,它也會被透明地重啓,並且其對應的 connection 也會一直有效。但是如果 XPC service 是在接收消息時 crash 了的話,App 需用重新發送該消息才能接受到對應的響應。這就是爲什麼要調用 remoteObjectProxyWithErrorHandler 方法來設置錯誤處理函數了。

這個方法接受一個閉包作爲參數,在發生錯誤的時候被執行。XPC API 保證在錯誤處理裏的閉包或者是消息響應裏的閉包之中,只有一個會被執行;如果消息消息響應裏的閉包被執行了,那麼錯誤處理的就不會被執行,反之亦然。這樣就使得資源清理變得容易了。

突然終止 (Sudden Termination)

XPC 是通過跟蹤那些是否有仍在處理請求來管理 service 的生命週期的,如果有請求正在運行,對應的 service 不會被停止。如果消息請求的響應還沒有被髮送,則這個請求會被認爲是正在運行的。對於那些沒有 reply 的處理程序的請求,只要方法體還在運行,這個請求就會被認爲是正在運行的。

在某些情況下,我們可能想告訴 XPC 我們還有更多的工作要做,我們可以使用 NSProcessInfo 的 API 來實現這一點:

func disableAutomaticTermination(reason: String!)
func enableAutomaticTermination(reason: String!)

如果 XPC service 接受傳入請求並需要在後臺執行一些異步操作,這些 API 就能派上用場了(即告訴系統不希望被突然終止)。某些情況下我們還可能需要調整我們的 QoS (服務質量)設置。

中斷 (Interruption) 和失效 (Invalidation)

XPC 的最常見的用法是 App 發消息給它的 XPC service。XPC 允許非常靈活的設置。我們通過下文會瞭解到,connection 是雙向的,它可以是匿名監聽者 (anonymous listeners)。如果另一端消失了(因爲 crash 或者是正常的進程終止),這時連接將很有可能變得無效。我們可以給 connection 對象設置失效處理函數,如果 XPC runtime 無法重新創建這個 connection,我們的失效處理函數將會被執行。

我們還可以給 connection 設置中斷處理程序,會在 connection 被中斷的時候會執行,儘管此時 connection 仍然是有效的。

在 NSXPCConnection中 對應的兩個屬性是:

var interruptionHandler: (() -> Void)!
var invalidationHandler: (() -> Void)!

雙向連接 (Bidirectional Connections)

一個經常被忽略而又有意思的事實是:connection 是雙向的。但是隻能通過 App 創建到 service 的初始連接。service 不能主動創建到 App 的連接(見下文的 service lookup)。一旦連接已經建好了,兩端都可以發起請求。

正如 service 端給 connection 對象設置了 exportedObject,App 端也可以這麼做。這樣可以讓 service 端通過  remoteObjectProxy 來和 App 的 exported object 進行通信了。值得注意是,XPC service 由系統管理其生命週期,如果沒有未完成的請求,可能會被停止掉(參見上文的 Sudden Termination)。

服務查找 (Service Lookup)

當我們連接到 XPC service 的時候,我們需要找到連接的另一端。對於使用私有 XPC service 的 App,XPC 會在 App 的 bundle 範圍內通過名字查找。還有其他的方法來連接到 XPC,讓我們來看看所有的可能性。

XPC Service

假如 App 使用:

NSXPCConnection(serviceName: "io.objc.myapp.myservice")

XPC 會在 App 自己的命名空間 (namespace) 查找名爲 io.objc.myapp.myservice 的service,這樣的 service 僅對當前 App 有效,其他 App 無法連接。XPC service bundle 要麼是位於 App 的 bundle 裏,要麼是在該 App 使用的 Framework 的 bundle 裏。

Mach Service

另一個選擇是使用:

NSXPCConnection(machServiceName: "io.objc.mymachservice", options: NSXPCConnectionOptions(0))

這會在當前用戶的登錄會話 (login session) 中查找名爲 io.objc.mymachservice 的service。 我們可以在 /Library/LaunchAgents 或 ~/Library/LaunchAgents 目錄下安裝 launch agent,這些 launch agent 也以與 App 裏的 XPC service 幾乎相同的方式來提供 service。由於 launch agent 會在 per-login session 中啓動的,在同一個登錄會話中運行的多個 App 可以和同一個 launch agent 進行通信。

這種方法很有用,例如狀態欄 (Status Bar) 中的 menu extra 程序(即右上角只有菜單項的 App)需要和 UI App 進行通信的時候。普通 App 和 menu extra 程序都可以和同一個 launch agent 進行通信並交互數據。當你需要讓兩個以上的進程需要相互通信,XPC 可以是一個非常優雅的方案。

假設我們要寫一個天氣類的 App,我們可以把天氣數據的抓取和解析做成 launch agent 方式的 XPC service。我們可以分別創建 menu extra 程序,普通 App,以及通知中心的 Widget 來顯示同樣的天氣數據。它們都可以通過 NSXPCConnection 和同一個 launch agent 進行通信。

與 XPC service 相同,launch agent 的生命週期也可以完全由 XPC 掌控:按需啓動,閒置或者系統內存不足的時候停止。

匿名監聽者 (Anonymous Listeners) 和端點 (Endpoints)

XPC 有通過 connection 來傳遞被稱爲 listener endpoints 的能力。這個概念一開始會讓人非常費解,但是它可以帶來更大的靈活性。

比如說我們有兩個 App,我們希望它們能夠過 XPC 來互相通信,每個 App 都不知道其他 App 的存在,但它們都知道相同的一個(共享)launch agent。

這兩個 App 可以先連接到 launch agent。App A 創建一個被稱爲 匿名監聽者 (anonymous listener) 的對象,並通過 XPC 發送一個端點 (endpoint),並由匿名監聽者創建的對象給 launch agent。App B 可以通過 XPC 在同樣的 launch agent 中拿到這個 endpoint。這時,App B 就可以直接連接到這個匿名監聽者,即 App A。

在 App A 創建一個 anonymous listener:

let listener = NSXPCListener.anonymousListener()

類似於 XPC service 創建普通的 listener。然後從這個 listener 創建一個 endpoint:

let endpoint = listener.endpoint

這個 endpoint 可以通過 XPC 來傳遞(實現了 NSSecureCoding 協議 )。一旦 App B 獲取到這個 endpoint,它可以創建到 App A 的 listener 的一個 connection:

let connection = NSXPCConnection(listenerEndpoint: endpoint)

Privileged Mach Service

最後一個選擇是使用:

NSXPCConnection(machServiceName: "io.objc.mymachservice", options: .Privileged)

這種方式和 launch agent 非常類似,不同的是創建了到 launch daemon 的 connection。launch agent 進程是 per user 的,它們以用戶的身份運行在用戶的登錄會話 (login session) 中。守護進程 (Daemon) 則是 per machine 的,即使當前多個用戶登錄,一個 XPC daemon 也只有一個實例運行。

如果要運行 daemon 的話,有很多安全相關的問題需要考慮。雖然以 root 權限運行 daemon 是可能的,但是最好是不要這麼這麼做。我們可能更希望它以一些獨特的用戶身份來運行。具體可以參考 TN2083 - Designing Secure Helpers and Daemons。大多數情況,我們並不需要 root 權限。

文件訪問 (File Access)

假設我們要創建一個 HTTP 文件下載的 service。我們需要允許 service 能發起對外的網絡連接請求。不太明顯的是,我們可以讓 service 下載寫入文件而不需要訪問任何文件。

它是如何做到的呢,首先我們在 App 中創建這個將被下載的文件,然後給這個文件創建一個文件句柄 (file handle):

let fileURL = NSURL.fileURLWithPath("/some/path/we/want/to/download/to")
if NSData().writeToURL(fileURL, options:0, error:&error) {
    let maybeHandle = NSFileHandle.fileHandleForWritingToURL(url:fileURL, error:&error)
    if let handle = maybeHandle {
        self.startDownload(fromURL:someURL, toFileHandle: handle) {
            self.downloadComplete(url: someURL)
        }
    }
}


func startDownload(fromURL: NSURL, toFileHandle: NSFileHandlehandle, completionHandler: (NSURL)->Void) -> Void

然後將這個文件句柄傳給 remote object proxy,實際上就是通過 XPC connection 傳給了 service,service 通過這個文件句柄寫入內容,就可以保存到實際的文件中了。

同樣,我們可以在一個進程中打開用於讀取數據的 NSFileHandle 對象,然後傳給另外一個進程,這樣就可以做到那個進程不需要直接訪問文件也能讀取其內容了。

移動數據 (Moving Data)

雖然 XPC 非常高效,但是進程間消息傳遞並不是免費的。如果你需要通過 XPC 傳遞大量的二進制數據,你可以使用這些技巧。

正常情況下使用的 NSData 對象會在傳遞到另一端會被複制一份。對於較大的二進制數據,更有效的方法是使用 內存映射 (memory-mapped) 數據。WWDC 2013 session 702 的slides 從 57 頁開始介紹瞭如何發送大量數據

XPC 有個技巧,能夠保證數據在進程間傳遞不會被複制。訣竅就是利用 dispatch_data_t 和 NSData 是 toll-free bridged 的。創建內存映射的 dispatch_data_t 實例與之配合,就可以高效的通過 XPC 來傳遞了。看上去是這樣:

let size: UInt = 8000
let buffer = mmap(nil, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0)
let ddata = dispatch_data_create(buffer, size, DISPATCH_TARGET_QUEUE_DEFAULT, _dispatch_data_destructor_munmap)
let data = ddata as NSData

調試 (Debugging)

Xcode 支持通過 XPC 方式的進程間通信的調試。如果你在內嵌在 App 的私有 XPC service 裏設置一個斷點,調試器將以你期望的方式在斷點處停下來。

請務必看看 Activity Tracing。這組 API 定義在在頭文件 os/activity.h 中,提供了一種能夠跨越上下文執行和進程邊界的傳遞方式,來查看到底是什麼引起了所需要執行的行爲。WWDC 2014 session 714, Fix Bugs Faster Using Activity Tracing,對此做了很好的介紹。

常見錯誤 (Common Mistakes)

一個最常見的錯誤是沒有調用 connection 或者 listener 的 resume 方法。記得它們創建後都是被掛起狀態。

如果 connection 無效,很大的可能是因爲配置錯誤導致的。請檢查 bundle id 是不是和 service 名字相匹配,代碼中是否指定了正確的 service 名字。

調試守護進程 (Debugging Daemons)

調試 daemon 會稍微複雜一些,但它仍然可以很好的工作。daemon會被 launchd 進程啓動。所以需要分兩部設置:在開發過程中,修改我們 daemon 的 launchd.plist,設置 WaitForDebugger 爲true。然後在 Xcode 中,修改 daemon 的 scheme,在 scheme editor -> Run -> Info 頁下可以修改 Launch 方式,從 “Automatically” 改到 “Wait for executable to be launched.”

現在通過 Xcode 運行 daemon,daemon 不會被啓動,但是調試器會一直等着直到它啓動爲止。一旦 launchd 啓動了 daemon,調試器會自動連接上,我們就可以開始幹活了。

Connection 的安全屬性

每個 NSXPCConnection 具有這些屬性

var auditSessionIdentifier: au_asid_t { get }
var processIdentifier: pid_t { get }
var effectiveUserIdentifier: uid_t { get }
var effectiveGroupIdentifier: gid_t { get }

來描述這個 connection。在 listener 端,如在 agent 或者 daemon 中,可以利用這些屬性來查看誰在嘗試進行連接,可以基於這些屬性來決定是否允許這些連接。對於在 App bundle 裏的私有 XPC service,上面的屬性完全可以無視,因爲只有當前 App 可以查找到這個 service。

xpc_connection_create(3) 的 man page 中有一章 “Credentials”,介紹了一些使用這些 API 缺點,在使用時需要多加小心。

QoS 和 Boosts

在 OS X 10.10 中,Apple 提出了 Quality of Service (QoS) 概念。可以用來輔助調解如給 UI 較高優先級,並降低後臺行爲的優先級。當 QoS 遇到 XPC service,事情就變得有趣了 - 想想 XPC service 一般是完成什麼樣的工作?

QoS 會跨進程傳送 (propagates),在大多數情況下我們都不需要擔心。當 UI 線程發起一個 XPC 調用時,service 會以 boosted QoS 來運行;但是如果 App 中的後臺線程發起 XPC 調用,這也會影響到 service 的 QoS,它會以較低的 QoS 來運行。

WWDC 2014 session 716, Power, Performance and Diagnostics ,介紹了很多關於 QoS 的內容。其中它就提到了如何使用 DISPATCH_BLOCK_DETACHED 來分離當前的QoS,即如何防止 QoS propagates。

所以當 XPC service 因爲某些請求的副作用而開始一些不相關的工作時,必須確保它從 QoS 中分離

低階API (Lower-Level API)

NSXPCConnection 所有的 API 都是建立 C API 之上,可以在 xpc(3) man page 和子頁面中找到它的文檔。

我們可以使用 C API 來爲 App 創建 XPC service,只要兩端都使用 C API 就好。

在概念上 C API 和 Foundation 的 API 很相似(譯者注:實際上是 C API 在 10.7 中被率先引入),稍微令人困惑的一點是,C API 中一個 connection 可以同時做爲一個接受傳入連接請求的 listener ,或者是到另一個進程的 connection。

事件流 (Event Streams)

目前只有 C API 提供的一個特性是,支持對於 IOKit events,BSD notifications,或者 Core Foundation 的distributed notifications 的 launch-on-demand(按需啓動)。這些在事件或者通知在 launch agent/daemons 也是可以使用的。

在 xpc_events(3) man page 中列出了這些事件流。通過 C API,可以相對簡單的實現一個當特定的硬件連接後按需啓動的一個後臺進程 (launch agent)。

--------------------------------

原文 XPC

譯者簡介

徐濤

Indie Mac/iOS Developer

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章