Mach Ports Distrbuted Notifications Distrbuted Objects AppleEvents & AppleScript Pasteboard XPC
所有进程间通信最终落实依赖的还是Mach 内核 API 提供的功能。
Mach Port
是在系统内核实现和维护的一种 IPC 消息队列,持有用于 IPC 通信的 mach messages。
只有一个进程可以从对应的 port 里 dequeue 一条消息,这个进程被持有接收权利(receive-right)。
可以有多个进程往某个 port 里 enqueue 消息,这些进程持有该 port 的发送权利(send-rights)。
我们可以简单把 mach port 看做是一个单向的数据发送渠道,构建一个消息结构体后通过mach_msg()
方法发出去。
因为只能单向发送,所以当 B 进程收到了 A 进程发来的消息之后要自己创建一个新的 Port 然后又发回去 A 进程。
Mach 端口发送一个信息调用一次 mach_msg_send
发
送之前需要做初始化。
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)
};
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) {
// ...
}
接收消息的时候,只需要声明不用初始化
mach_port_t port;
struct {
mach_msg_header_t header;
mach_msg_body_t body;
mach_msg_type_descriptor_t type;
mach_msg_trailer_t trailer;
} message;
mach_msg_return_t error = mach_msg_receive(&message.header);
if (error == MACH_MSG_SUCCESS) {
natural_t data = message.type.pad1;
// ...
}
CFMessagePort
确实非常适合用于简单的一对一通讯。简简单单几行代码,一个本地端口就被附属到runloop源上,只要获取到消息就执行回调。
static CFDataRef Callback(CFMessagePortRef port,
SInt32 messageID,
CFDataRef data,
void *info)
{
// ...
}
CFMessagePortRef localPort =
CFMessagePortCreateLocal(nil,
CFSTR("com.example.app.port.server"),
Callback,
nil,
nil);
CFRunLoopSourceRef runLoopSource =
CFMessagePortCreateRunLoopSource(nil, localPort, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(),
runLoopSource,
kCFRunLoopCommonModes);