您好,欢迎来到二三四教育网。
搜索
您的当前位置:首页RunLoop的wakeup port

RunLoop的wakeup port

来源:二三四教育网

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的回调函数。

Copyright © 2019- how234.cn 版权所有 赣ICP备2023008801号-2

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务