美文网首页
如何知道一个锁到底被哪个线程占用?

如何知道一个锁到底被哪个线程占用?

作者: 蓝天白云_Sam | 来源:发表于2024-01-20 12:23 被阅读0次

    解决思路

    对于某几种(常用的)锁,操作系统会记录其持有线程(owner)是谁的信息。
    那么究竟是怎么记的呢?我们能不能直接从锁的实例拿到这个owner信息呢?

    pthread_mutex 互斥锁

    互斥锁对应的结构为pthread_mutex_t,如下

    typedef struct _pthread_mutex
    {
        long           sig;       /* Unique signature for this structure */
        pthread_lock_t lock;          /* Used for internal mutex on structure */
        union {
            uint32_t value;
            struct _pthread_mutex_options options;
        } mtxopts;
        int16_t       prioceiling;
        int16_t        priority;      /* Priority to restore when mutex unlocked */
        uint32_t      waiters;       /* Count of threads waiting for this mutex */
        pthread_t      owner;         /* Which thread has this mutex locked */
        struct _pthread_mutex *next, *prev;  /* List of other mutexes he owns */
        struct _pthread_cond *busy;   /* List of condition variables using this mutex */
        semaphore_t    sem;       /* Semaphore used for waiting */
        semaphore_t order;
    } pthread_mutex_t;
    

    pthread_mutex_t 有一个保存持有该锁线程tid的变量:owner,其中owner的获取方法如下

    owner_tid = *(int *)((char *)&mutex + 0x18)
    

    当pthread_mutex 等待锁的时候,会停留在__psynch_mutexwait 函数,对应的锁会放在寄存器x19中,持有该锁的tid获取方法如下

    (lldb)  p/x *((int*)($x19 + 0x18))
    (int) 0x001f734e
    

    对于NSLock,如果知道对应NSLock的地址,则可以通过NSLock获取到对应的tid,方法如下

    (lldb) p/x *((int*)((char*)self->_lock + 0x20))
    (int) 0x001f734e
    

    通过thread list打印当前线程列表,可知该锁被thread #7持有

    (lldb) thread list
    Process 4551 stopped
    * thread #1: tid = 0x1f725d, 0x00000001fbe09ca8 libsystem_kernel.dylib`__psynch_mutexwait + 8, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
      thread #3: tid = 0x1f734a, 0x000000021e6bb9fc libsystem_pthread.dylib`start_wqthread
      thread #7: tid = 0x1f734e, 0x00000001fbe09978 libsystem_kernel.dylib`__semwait_signal + 8, queue = 'com.apple.root.default-qos'
      thread #8: tid = 0x1f734f, 0x00000001fbe09178 libsystem_kernel.dylib`mach_msg2_trap + 8, name = 'com.apple.uikit.eventfetch-thread'
      thread #9: tid = 0x1f7350, 0x000000021e6bb9fc libsystem_pthread.dylib`start_wqthread
      thread #10: tid = 0x1f7351, 0x000000021e6bb9fc libsystem_pthread.dylib`start_wqthread
    

    _psynch_mutexwait的定义在darwin-libpthreadkern_synch.c 文件,定义如下,tid为mutex的持有线程tid

    int _psynch_mutexwait(__unused proc_t p, user_addr_t mutex, uint32_t mgen, uint32_t ugen, uint64_t tid, uint32_t flags, uint32_t *retval)
    

    根据ARM汇编的参数传递,tid保存在x3寄存器:

    (lldb) p/x $x3
    (unsigned long) 0x00000000001f734e
    

    _pthread_mutex_firstfit_lock_wait(pthread_mutex_t *mutex, mutex_seq newseq, uint64_t oldtid) 实现在pthread_mutex.c
    调用栈如下:

    (lldb) bt
    * thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
        frame #0: 0x00000001fbe09ca8 libsystem_kernel.dylib`__psynch_mutexwait + 8
        frame #1: 0x000000021e6bbd68 libsystem_pthread.dylib`_pthread_mutex_firstfit_lock_wait + 84
      * frame #2: 0x000000021e6bb7f0 libsystem_pthread.dylib`_pthread_mutex_firstfit_lock_slow + 248
        frame #3: 0x00000001009b810c WeTest`-[ViewController viewDidLoad](self=0x0000000101907e10, _cmd="viewDidLoad") at ViewController.m:27:5
        frame #4: 0x00000001b6b61c8c UIKitCore`-[UIViewController _sendViewDidLoadWithAppearanceProxyObjectTaggingEnabled] + 84
        frame #5: 0x00000001b6916854 UIKitCore`-[UIViewController loadViewIfRequired] + 936
        frame #6: 0x00000001b6915048 UIKitCore`-[UIViewController view] + 24
        frame #7: 0x00000001b6988084 UIKitCore`-[UIWindow addRootViewControllerViewIfPossible] + 136
        frame #8: 0x00000001b6987f78 UIKitCore`-[UIWindow _updateLayerOrderingAndSetLayerHidden:actionBlock:] + 216
        frame #9: 0x00000001b6987c78 UIKitCore`-[UIWindow _setHidden:forced:] + 256
        frame #10: 0x00000001b6987b04 UIKitCore`-[UIWindow _mainQueue_makeKeyAndVisible] + 40
        frame #11: 0x00000001b6becd5c UIKitCore`-[UIWindowScene _performDeferredInitialWindowUpdateForConnection] + 208
        frame #12: 0x00000001b6a93e14 UIKitCore`+[UIScene _sceneForFBSScene:create:withSession:connectionOptions:] + 1232
        frame #13: 0x00000001b6b50e8c UIKitCore`-[UIApplication _connectUISceneFromFBSScene:transitionContext:] + 808
        frame #14: 0x00000001b6b509b4 UIKitCore`-[UIApplication workspace:didCreateScene:withTransitionContext:completion:] + 360
        frame #15: 0x00000001b6b507e0 UIKitCore`-[UIApplicationSceneClientAgent scene:didInitializeWithEvent:completion:] + 288
        frame #16: 0x00000001ccb696d4 FrontBoardServices`-[FBSScene _callOutQueue_didCreateWithTransitionContext:completion:] + 324
        frame #17: 0x00000001ccb69570 FrontBoardServices`__92-[FBSWorkspaceScenesClient createSceneWithIdentity:parameters:transitionContext:completion:]_block_invoke.108 + 280
        frame #18: 0x00000001ccb6819c FrontBoardServices`-[FBSWorkspace _calloutQueue_executeCalloutFromSource:withBlock:] + 168
        frame #19: 0x00000001ccb73f8c FrontBoardServices`__92-[FBSWorkspaceScenesClient createSceneWithIdentity:parameters:transitionContext:completion:]_block_invoke + 352
        frame #20: 0x000000010174eb34 libdispatch.dylib`_dispatch_client_callout + 20
        frame #21: 0x0000000101752530 libdispatch.dylib`_dispatch_block_invoke_direct + 300
        frame #22: 0x00000001ccb64520 FrontBoardServices`__FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 52
        frame #23: 0x00000001ccb644a0 FrontBoardServices`-[FBSMainRunLoopSerialQueue _targetQueue_performNextIfPossible] + 240
        frame #24: 0x00000001ccb64378 FrontBoardServices`-[FBSMainRunLoopSerialQueue _performNextFromRunLoopSource] + 28
        frame #25: 0x00000001b470d12c CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 28
        frame #26: 0x00000001b470c3a8 CoreFoundation`__CFRunLoopDoSource0 + 176
        frame #27: 0x00000001b470abbc CoreFoundation`__CFRunLoopDoSources0 + 340
        frame #28: 0x00000001b4709898 CoreFoundation`__CFRunLoopRun + 828
        frame #29: 0x00000001b4709478 CoreFoundation`CFRunLoopRunSpecific + 608
        frame #30: 0x00000001f7c624f8 GraphicsServices`GSEventRunModal + 164
        frame #31: 0x00000001b6b2d62c UIKitCore`-[UIApplication _run] + 888
        frame #32: 0x00000001b6b2cc68 UIKitCore`UIApplicationMain + 340
        frame #33: 0x00000001009b85d4 WeTest`main(argc=1, argv=0x000000016f44b7d8) at main.m:17:12
        frame #34: 0x00000001d742edcc dyld`start + 2240
    

    NSLock

    对于NSLock 类型对象 lock:

    1. 对应的pthread_mutex_t的地址为&lock + 0x8
    2. 对应的tid地址为&lock + 0x20,

    参考文献

    1. 如何知道一个锁到底被哪个线程占用?
    2. ARMv8-aarch64 寄存器和指令集
    3. libsystem_pthread源码: darwin-libpthread

    相关文章

      网友评论

          本文标题:如何知道一个锁到底被哪个线程占用?

          本文链接:https://www.haomeiwen.com/subject/hgxjodtx.html