RunLoop的wakeup port起什么作用?
在查看RunLoop内容时,会显示wakeup port,如下所示:
(lldb) po [NSRunLoop currentRunLoop]
<CFRunLoop 0x6000001eed00 [0x10abbbc80]>{wakeup port = 0x2503, stopped = false, ignoreWakeUps = false,
current mode = kCFRunLoopDefaultMode,
common modes = <CFBasicHash 0x60000024f990 [0x10abbbc80]>{type = mutable set, count = 2,
entries =>
0 : <CFString 0x10bf2be88 [0x10abbbc80]>{contents = "UITrackingRunLoopMode"}
2 : <CFString 0x10ab91818 [0x10abbbc80]>{contents = "kCFRunLoopDefaultMode"}
}
我们知道RunLoop中的Source 1是基于mach port的,但是RunLoop的wakeup port是做什么的?
由于RunLoop在睡眠状态下是通过mach port唤醒的,如CFRunLoop.c的__CFRunLoopRun方法中下面代码所示:
do {
if (kCFUseCollectableAllocator) {
// objc_clear_stack(0);
// <rdar://problem/16393959>
memset(msg_buffer, 0, sizeof(msg_buffer));
}
msg = (mach_msg_header_t *)msg_buffer;
// 处理唤醒的mach消息
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
// Drain the internal queue. If one of the callout blocks sets the timerFired flag, break out and service the timer.
while (_dispatch_runloop_root_queue_perform_4CF(rlm->_queue));
if (rlm->_timerFired) {
// Leave livePort as the queue port, and service timers below
rlm->_timerFired = false;
break;
} else {
if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
}
} else {
// Go ahead and leave the inner loop.
break;
}
} while (1);
由于RunLoop的唤醒是通过mach port唤醒,而Source 0不是基于mach port的source,激活Source 0的操作需要如下两个步骤,缺一不可:
CFRunLoopSourceSignal(source0);
CFRunLoopWakeUp(runLoop);
那么是否是在CFRunLoopWakeUp(runLoop)中通过mach_msg发送了消息?查看CFRunLoopWakeUp的一部分汇编,在其中发现调用了mach_msg函数,发送(或接收)了消息:
0x1101a42d2 <+194>: mov qword ptr [rbp - 0x120], rcx
0x1101a42d9 <+201>: mov dword ptr [rbp - 0x118], eax
0x1101a42df <+207>: mov dword ptr [rbp - 0x114], 0x0
0x1101a42e9 <+217>: mov dword ptr [rbp - 0x10c], 0x0
0x1101a42f3 <+227>: mov dword ptr [rsp], 0x0
0x1101a42fa <+234>: lea r15, [rbp - 0x120]
0x1101a4301 <+241>: mov esi, 0x11
0x1101a4306 <+246>: mov edx, 0x18
0x1101a430b <+251>: xor ecx, ecx
0x1101a430d <+253>: xor r8d, r8d
0x1101a4310 <+256>: xor r9d, r9d
0x1101a4313 <+259>: mov rdi, r15
0x1101a4316 <+262>: call 0x1102f051e ; symbol stub for: mach_msg
在执行完mach_msg之后,查看mach_msg的第一个参数mach_msg_header_t *msg来确认发送的内容,
(lldb) memory read -s8 -c3 -fx $r15
0x70000fa72ae0: 0x0000001800000013 0x0000000000002503
0x70000fa72af0: 0x000000000fa72b30
mach_msg_header_t的存储结构如下所示
typedef struct
{
mach_msg_bits_t msgh_bits; // unsigned int
mach_msg_size_t msgh_size; // unsigned int
mach_port_t msgh_remote_port; // unsigned int
mach_port_t msgh_local_port; // unsigned int
mach_port_name_t msgh_voucher_port; // mach_port_name_t
mach_msg_id_t msgh_id; // int
} mach_msg_header_t;
观察这一段“0x0000000000002503”,由于存储方式是小端存储,所以msgh_remote_port对应的内容为0x2503,正好是runLoop的wakeup port。
再查看mach_msg的第二个参数mach_msg_option_t option,第二个参数的值可以根据"0x1101a4301 <+241>: mov esi, 0x11"这条指令获得,所以option值为0x11,是由"define MACH_SEND_MSG 0x00000001"和"#define MACH_SEND_TIMEOUT 0x00000010"参数或运算的结果,意思是发送消息。
所以CFRunLoopWakeUp(runLoop)确实向runLoop的wakeup port发送了消息从而唤醒了runLoop,执行Source 0的回调函数。
网友评论