iOS 主程序和主隊(duì)列的區(qū)別

今天看了iOS主線程和主隊(duì)列的區(qū)別 的文章。這里主要涉及到兩個(gè)函數(shù)祖很,突然對(duì)這兩個(gè)概念產(chǎn)生了興趣累盗,因此想驗(yàn)證下作者說的是否正確。想看懂別人寫的代碼最起碼要弄懂寫的啥意思吧突琳。這里有兩個(gè)函數(shù)不太懂若债。先研究下面這兩個(gè)函數(shù)

  • void *_Nullable dispatch_get_specific(const void *key);
  • void dispatch_queue_set_specific(dispatch_queue_t queue, const void *key, void *_Nullable context, dispatch_function_t _Nullable destructor);

void dispatch_queue_set_specific(dispatch_queue_t queue, const void *key, void *_Nullable context, dispatch_function_t _Nullable destructor);

這個(gè)函數(shù)是給指定隊(duì)列設(shè)置值的,我們看看具體是怎么保存值的拆融。

DISPATCH_NOINLINE
void
dispatch_queue_set_specific(dispatch_queue_t dq, const void *key,
    void *ctxt, dispatch_function_t destructor)
{
    if (slowpath(!key)) {
        return;
    }
    dispatch_queue_specific_t dqs;

    dqs = _dispatch_calloc(1, sizeof(struct dispatch_queue_specific_s));
    dqs->dqs_key = key;
    dqs->dqs_ctxt = ctxt;
    dqs->dqs_destructor = destructor;
    if (slowpath(!dq->dq_specific_q)) {
        _dispatch_queue_init_specific(dq);
    }
    _dispatch_barrier_trysync_or_async_f(dq->dq_specific_q, dqs,
            _dispatch_queue_set_specific, 0);
}

上面函數(shù)_dispatch_barrier_trysync_or_async_f 最終會(huì)調(diào)用到下面函數(shù)

static void
_dispatch_queue_set_specific(void *ctxt)
{
    dispatch_queue_specific_t dqs, dqsn = ctxt;
    dispatch_queue_specific_queue_t dqsq =
            (dispatch_queue_specific_queue_t)_dispatch_queue_get_current();

    TAILQ_FOREACH(dqs, &dqsq->dqsq_contexts, dqs_list) {
        if (dqs->dqs_key == dqsn->dqs_key) {
            // Destroy previous context for existing key
            if (dqs->dqs_destructor) {
                dispatch_async_f(_dispatch_get_root_queue(
                        DISPATCH_QOS_DEFAULT, false), dqs->dqs_ctxt,
                        dqs->dqs_destructor);
            }
            if (dqsn->dqs_ctxt) {
                // Copy new context for existing key
                dqs->dqs_ctxt = dqsn->dqs_ctxt;
                dqs->dqs_destructor = dqsn->dqs_destructor;
            } else {
                // Remove context storage for existing key
                TAILQ_REMOVE(&dqsq->dqsq_contexts, dqs, dqs_list);
                free(dqs);
            }
            return free(dqsn);
        }
    }
    // Insert context storage for new key
    TAILQ_INSERT_TAIL(&dqsq->dqsq_contexts, dqsn, dqs_list);
}
image.png

從這個(gè)函數(shù)中我們能看出具體結(jié)構(gòu)的
獲取 specific queue蠢琳,這相當(dāng)于map 的key 值,value 是array。array 中裝有dispatch_queue_specific_t 對(duì)象镜豹,我們通過判斷dispatch_queue_specific_t對(duì)象的dqs_key 檢查是否dispatch_queue_specific_t 對(duì)象是否相等傲须。相等重新設(shè)置dispatch_queue_specific_t 的dqs_ctxt,不相等趟脂,直接插入到array只能中泰讽。結(jié)構(gòu)如下

image.png

void *_Nullable dispatch_get_specific(const void *key);

DISPATCH_NOINLINE
void *
dispatch_queue_get_specific(dispatch_queue_t dq, const void *key)
{
    if (slowpath(!key)) {
        return NULL;
    }
    return _dispatch_queue_get_specific_inline(dq, key);
}

這里出現(xiàn)了slowpath,這個(gè)干嗎用的呢?全局搜索,是宏定義,如下

#if __GNUC__
#define _safe_cast_to_long(x) \
        ({ _Static_assert(sizeof(typeof(x)) <= sizeof(long), \
                "__builtin_expect doesn't support types wider than long"); \
                (long)(x); })
#define fastpath(x) ((typeof(x))__builtin_expect(_safe_cast_to_long(x), ~0l))
#define slowpath(x) ((typeof(x))__builtin_expect(_safe_cast_to_long(x), 0l))
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#else
#define fastpath(x) (x)
#define slowpath(x) (x)
#define likely(x) (!!(x))
#define unlikely(x) (!!(x))
#endif // __GNUC__

這里的__builtin_expect用法已卸,可以看我這篇文章__builtin_expect 總結(jié)

從這個(gè)函數(shù)中調(diào)用了_dispatch_queue_get_specific_inline佛玄, 最終會(huì)調(diào)用到
*static void _dispatch_queue_get_specific(void ctxt)

static void
_dispatch_queue_get_specific(void *ctxt)
{
    void **ctxtp = ctxt;
    void *key = *ctxtp;
    dispatch_queue_specific_queue_t dqsq =
            (dispatch_queue_specific_queue_t)_dispatch_queue_get_current();
    dispatch_queue_specific_t dqs;

    TAILQ_FOREACH(dqs, &dqsq->dqsq_contexts, dqs_list) {
        if (dqs->dqs_key == key) {
            *ctxtp = dqs->dqs_ctxt;
            return;
        }
    }
    *ctxtp = NULL;
}

看這里就是set設(shè)置的逆運(yùn)算

DISPATCH_NOINLINE void *dispatch_queue_get_specific(dispatch_queue_t dq, const void *key) 查詢的是當(dāng)前queue。
DISPATCH_NOINLINE void * dispatch_get_specific(const void *key) 查詢的是指定queue累澡。

DISPATCH_NOINLINE
void *
dispatch_get_specific(const void *key)
{
    if (slowpath(!key)) {
        return NULL;
    }
    void *ctxt = NULL;
    dispatch_queue_t dq = _dispatch_queue_get_current();

    while (slowpath(dq)) {
        ctxt = _dispatch_queue_get_specific_inline(dq, key);
        if (ctxt) break;
        dq = dq->do_targetq;
    }
    return ctxt;
}

驗(yàn)證想法
測(cè)試代碼

    static void *queueKey1 = "queueKey1";
    dispatch_queue_t queue1 = dispatch_queue_create(queueKey1, DISPATCH_QUEUE_SERIAL);
    dispatch_queue_set_specific(queue1, queueKey1, queueKey1, NULL);
    
  void * value =   dispatch_get_specific(queueKey1);
    NSLog(@"current queue %s" ,value);
    value= dispatch_queue_get_specific(queue1, queueKey1);
    NSLog(@"指定 queue %s" ,value);
  dispatch_queue_t mainQueue = dispatch_get_main_queue();
    
    dispatch_queue_set_specific(mainQueue, queueKey1, queueKey1, NULL);
    value =   dispatch_get_specific(queueKey1);
    NSLog(@"current queue %s" ,value);

測(cè)試結(jié)果

2018-09-19 14:46:55.122522+0800 PopPlayground[47991:16410734] current queue (null)
2018-09-19 14:46:55.122678+0800 PopPlayground[47991:16410734] 指定 queue queueKey1
2018-09-19 14:46:55.122810+0800 PopPlayground[47991:16410734] current queue queueKey1

上面知識(shí)了解后我們開始看上篇博客哥們說的問題

第一題(主線程只會(huì)執(zhí)行主隊(duì)列的任務(wù)嗎梦抢?)

從這個(gè)題,我們能看出來愧哟,我們需要主線程和兩個(gè)隊(duì)列(主隊(duì)列和別的隊(duì)列)
測(cè)試代碼

int main(int argc, const char * argv[]) {
    
    
    const void * key = "queueKey";
    const void * mainValue = "mainValue";
    const void * serierValue = "serierValue";
    char * value ;
    dispatch_queue_t  mainQueue =    dispatch_get_main_queue();
    dispatch_queue_set_specific(mainQueue, key, mainValue, NULL);
    value = dispatch_queue_get_specific(mainQueue, key);
    NSLog(@"main queue value %s",value);
    dispatch_queue_t serier = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
    dispatch_queue_set_specific(serier, key, serierValue, NULL);
    value = dispatch_queue_get_specific(serier, key);
    NSLog(@"serier queue value %s",value);
    NSLog(@"current thread %@",NSThread.currentThread);
    
    dispatch_sync(serier, ^{
        NSLog(@"serier queue sync(同步) 執(zhí)行:  current thread %@ current queue %s",NSThread.currentThread,dispatch_get_specific(key));
    });
    dispatch_async(serier, ^{
        NSLog(@"serier queue async(異步) 執(zhí)行:current thread %@ current queue %s",NSThread.currentThread,dispatch_get_specific(key));
        
    });
    dispatch_async(mainQueue, ^{
        NSLog(@"main queue async(異步) 執(zhí)行:current thread %@ current queue %s",NSThread.currentThread,dispatch_get_specific(key));
    });
    
    //    dispatch_async(mainQueue, ^{
    //           NSLog(@"mian queue async(異步) 執(zhí)行:current thread %@ current queue %s",NSThread.currentThread,dispatch_get_specific(key));
    //    });
    dispatch_main();
    
    return 0;
}

測(cè)試結(jié)果

2018-09-20 15:16:16.045541+0800 TestCommand[90135:17393697] main queue value mainValue
2018-09-20 15:16:16.046208+0800 TestCommand[90135:17393697] serier queue value serierValue
2018-09-20 15:16:16.046618+0800 TestCommand[90135:17393697] current thread <NSThread: 0x100703530>{number = 1, name = main}
2018-09-20 15:16:16.046713+0800 TestCommand[90135:17393697] serier queue sync(同步) 執(zhí)行:  current thread <NSThread: 0x100703530>{number = 1, name = main} current queue serierValue
2018-09-20 15:16:16.046912+0800 TestCommand[90135:17393751] serier queue async(異步) 執(zhí)行:current thread <NSThread: 0x100500cb0>{number = 2, name = (null)} current queue serierValue
2018-09-20 15:16:16.046936+0800 TestCommand[90135:17393753] main queue async(異步) 執(zhí)行:current thread <NSThread: 0x10041be00>{number = 3, name = (null)} current queue mainValue

從測(cè)試結(jié)果我們能看出來

1 serier queue sync(同步) 執(zhí)行: current thread <NSThread: 0x1006036b0>{number = 1, name = main} current queue serierValue奥吩,結(jié)果代表主線程可以執(zhí)行其他隊(duì)列的任務(wù)。前提是在主線程的同步執(zhí)行其他隊(duì)列的任務(wù)蕊梧。
2 serier queue async(異步) 執(zhí)行:current thread <NSThread: 0x1028226e0>{number = 2, name = (null)} current queue serierValue 霞赫,發(fā)現(xiàn)異步執(zhí)行serier queue ,serier會(huì)自動(dòng)給我們生成一條NSTread,在該NSThread上執(zhí)行 該queue。
3 在主線程中同步執(zhí)行mainQueue(主隊(duì)列)的任務(wù)肥矢,發(fā)生死鎖绩脆。
4在主線程中可以異步執(zhí)行mainQueue的任務(wù)。

第二題(主隊(duì)列任務(wù)只會(huì)在主線程上執(zhí)行嗎)

這里我們需要主隊(duì)列和 兩條線程橄抹,主線程和其他線程靴迫。我們讓主隊(duì)列任務(wù)在其他線程中執(zhí)行。

int main(int argc, const char * argv[]) {
    
    const void * key = "queueKey";
    const void * mainValue = "mainValue";
    const void * serierValue = "serierValue";
    char * value ;
    dispatch_queue_t  mainQueue =    dispatch_get_main_queue();
    dispatch_queue_set_specific(mainQueue, key, mainValue, NULL);
    value = dispatch_queue_get_specific(mainQueue, key);
    NSLog(@"main queue value %s",value);
    dispatch_queue_t serier = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
    dispatch_queue_set_specific(serier, key, serierValue, NULL);
    value = dispatch_queue_get_specific(serier, key);
    NSLog(@"serier queue value %s",value);
    NSLog(@"current thread %@",NSThread.currentThread);
    
    NSThread * tread = [[NSThread alloc]initWithBlock:^{
        NSLog(@"current thread %@",NSThread.currentThread);
        dispatch_sync(mainQueue, ^{
            NSLog(@"other thread exe main queue sync(同步) 執(zhí)行:current thread %@ current queue %s",NSThread.currentThread,dispatch_get_specific(key));
        });
        dispatch_async(mainQueue, ^{
            NSLog(@"other thread exe main queue sync(異步) 執(zhí)行:current thread %@ current queue %s",NSThread.currentThread,dispatch_get_specific(key));
        });
    }];
    
    tread.name = @"nihao";
    [tread start];
    
    dispatch_async(mainQueue, ^{
               NSLog(@"main thead exe mian queue async(異步) 執(zhí)行:current thread %@ current queue %s",NSThread.currentThread,dispatch_get_specific(key));
    });
    dispatch_sync(serier, ^{
        NSLog(@"main thead exe serier queue sync(同步) 執(zhí)行:current thread %@ current queue %s",NSThread.currentThread,dispatch_get_specific(key));
    });
    
    dispatch_async(serier, ^{
        NSLog(@"main thead exe serier queue async(異步) 執(zhí)行:current thread %@ current queue %s",NSThread.currentThread,dispatch_get_specific(key));
    });
//    dispatch_sync(mainQueue, ^{
//        NSLog(@"ss main queue sync(同步) 執(zhí)行:current thread %@ current queue %s",NSThread.currentThread,dispatch_get_specific(key));
//    });
    dispatch_main();

    return 0;
}

執(zhí)行結(jié)果

2018-09-20 15:26:08.962266+0800 TestCommand[90753:17403728] main queue value mainValue
2018-09-20 15:26:08.962555+0800 TestCommand[90753:17403728] serier queue value serierValue
2018-09-20 15:26:08.962939+0800 TestCommand[90753:17403728] current thread <NSThread: 0x10050a630>{number = 1, name = main}
2018-09-20 15:26:08.964341+0800 TestCommand[90753:17403728] main thead exe serier queue sync(同步) 執(zhí)行:current thread <NSThread: 0x10050a630>{number = 1, name = main} current queue serierValue
2018-09-20 15:26:08.964403+0800 TestCommand[90753:17403875] current thread <NSThread: 0x100457e40>{number = 2, name = nihao}
2018-09-20 15:26:08.964456+0800 TestCommand[90753:17403873] main thead exe serier queue async(異步) 執(zhí)行:current thread <NSThread: 0x10071be40>{number = 3, name = (null)} current queue serierValue
2018-09-20 15:26:08.964506+0800 TestCommand[90753:17403872] main thead exe mian queue async(異步) 執(zhí)行:current thread <NSThread: 0x100455370>{number = 4, name = (null)} current queue mainValue
2018-09-20 15:26:08.964567+0800 TestCommand[90753:17403875] other thread exe main queue sync(同步) 執(zhí)行:current thread <NSThread: 0x100457e40>{number = 2, name = nihao} current queue mainValue
2018-09-20 15:26:08.964747+0800 TestCommand[90753:17403872] other thread exe main queue sync(異步) 執(zhí)行:current thread <NSThread: 0x100455370>{number = 4, name = (null)} current queue mainValue

1 從上述結(jié)果楼誓,我們發(fā)現(xiàn)玉锌,主線程異步 執(zhí)行了main queue,mainqueue 是在新生成的thread中執(zhí)行的疟羹。而不是我們想象中的主守,main queue 在主線程中執(zhí)行。(2018-09-20 15:26:08.964506+0800 TestCommand[90753:17403872] main thead exe mian queue async(異步) 執(zhí)行:current thread <NSThread: 0x100455370>{number = 4, name = (null)} current queue mainValue)
2 主線程異步執(zhí)行 serier queue 榄融,serier queue 也是在新的thread中執(zhí)行的 (2018-09-20 15:26:08.964456+0800 TestCommand[90753:17403873] main thead exe serier queue async(異步) 執(zhí)行:current thread <NSThread: 0x10071be40>{number = 3, name = (null)} current queue serierValue)
3 通過上面1 和 2 参淫,我們發(fā)現(xiàn)這里的主線程和serier 其實(shí)沒有什么區(qū)別的。 那為什么和我們的經(jīng)驗(yàn)不相符呢愧杯?(queue 任務(wù)是在主線程中執(zhí)行)

其實(shí)這都是沒有上下文導(dǎo)致的涎才。我們經(jīng)常說main queue 在主線程中執(zhí)行是因?yàn)槲覀兊腶pp 都已經(jīng)跑起來了,蘋果幫助我們已經(jīng)做了好多事情力九,將主線程和主隊(duì)列做了一個(gè)綁定關(guān)系耍铜。

探索 main queue 與主線程的綁定

我們將 [[NSRunLoop currentRunLoop]run]; 替換 dispatch_main();

int main(int argc, const char * argv[]) {
    const void * key = "queueKey";
    const void * mainValue = "mainValue";
    const void * serierValue = "serierValue";
    char * value ;
    dispatch_queue_t  mainQueue =    dispatch_get_main_queue();
    dispatch_queue_set_specific(mainQueue, key, mainValue, NULL);
    value = dispatch_queue_get_specific(mainQueue, key);
    NSLog(@"main queue value %s",value);
    dispatch_queue_t serier = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
    dispatch_queue_set_specific(serier, key, serierValue, NULL);
    value = dispatch_queue_get_specific(serier, key);
    NSLog(@"serier queue value %s",value);
    NSLog(@"current thread %@",NSThread.currentThread);
    
    NSThread * tread = [[NSThread alloc]initWithBlock:^{
        NSLog(@"current thread %@",NSThread.currentThread);
        dispatch_sync(mainQueue, ^{
            NSLog(@"other thread exe main queue sync(同步) 執(zhí)行:current thread %@ current queue %s",NSThread.currentThread,dispatch_get_specific(key));
        });
        dispatch_async(mainQueue, ^{
            NSLog(@"other thread exe main queue sync(異步) 執(zhí)行:current thread %@ current queue %s",NSThread.currentThread,dispatch_get_specific(key));
        });
    }];
    
    tread.name = @"nihao";
    [tread start];
    
    dispatch_async(mainQueue, ^{
        NSLog(@"main thead exe mian queue async(異步) 執(zhí)行:current thread %@ current queue %s",NSThread.currentThread,dispatch_get_specific(key));
    });
    dispatch_sync(serier, ^{
        NSLog(@"main thead exe serier queue sync(同步) 執(zhí)行:current thread %@ current queue %s",NSThread.currentThread,dispatch_get_specific(key));
    });
    
    dispatch_async(serier, ^{
        NSLog(@"main thead exe serier queue async(異步) 執(zhí)行:current thread %@ current queue %s",NSThread.currentThread,dispatch_get_specific(key));
    });
    //    dispatch_sync(mainQueue, ^{
    //        NSLog(@"ss main queue sync(同步) 執(zhí)行:current thread %@ current queue %s",NSThread.currentThread,dispatch_get_specific(key));
    //    });
    [[NSRunLoop currentRunLoop]run];
    return 0;
}

執(zhí)行結(jié)果如下

2018-09-20 15:37:25.231760+0800 TestCommand[91209:17414161] main queue value mainValue
2018-09-20 15:37:25.231973+0800 TestCommand[91209:17414161] serier queue value serierValue
2018-09-20 15:37:25.232494+0800 TestCommand[91209:17414161] current thread <NSThread: 0x100703420>{number = 1, name = main}
2018-09-20 15:37:25.233636+0800 TestCommand[91209:17414161] main thead exe serier queue sync(同步) 執(zhí)行:current thread <NSThread: 0x100703420>{number = 1, name = main} current queue serierValue
2018-09-20 15:37:25.233780+0800 TestCommand[91209:17414246] main thead exe serier queue async(異步) 執(zhí)行:current thread <NSThread: 0x10078bb80>{number = 3, name = (null)} current queue serierValue
2018-09-20 15:37:25.233805+0800 TestCommand[91209:17414254] current thread <NSThread: 0x10040fe20>{number = 2, name = nihao}
2018-09-20 15:37:25.233910+0800 TestCommand[91209:17414161] main thead exe mian queue async(異步) 執(zhí)行:current thread <NSThread: 0x100703420>{number = 1, name = main} current queue mainValue
2018-09-20 15:37:25.233951+0800 TestCommand[91209:17414161] other thread exe main queue sync(同步) 執(zhí)行:current thread <NSThread: 0x100703420>{number = 1, name = main} current queue mainValue
2018-09-20 15:37:25.234059+0800 TestCommand[91209:17414161] other thread exe main queue sync(異步) 執(zhí)行:current thread <NSThread: 0x100703420>{number = 1, name = main} current queue mainValue

1 我們發(fā)現(xiàn)只要我們?cè)?主線程中 運(yùn)行起來runloop ,我們發(fā)現(xiàn) 主線程 異步 main queue 的block 跌前,block 是在主線程中執(zhí)行的(2018-09-20 15:37:25.233910+0800 TestCommand[91209:17414161] main thead exe mian queue async(異步) 執(zhí)行:current thread <NSThread: 0x100703420>{number = 1, name = main} current queue mainValue)

看到這里我們main queue 棕兼,主線程和runloop 是有關(guān)系的。

mainqueue 的創(chuàng)建源碼

我們獲取主隊(duì)列都是通過dispatch_get_main_queue() 函數(shù)獲取

/*!
 * @function dispatch_get_main_queue
 *
 * @abstract
 * Returns the default queue that is bound to the main thread.
 *
 * @discussion
 * In order to invoke blocks submitted to the main queue, the application must
 * call dispatch_main(), NSApplicationMain(), or use a CFRunLoop on the main
 * thread.
 *
 * @result
 * Returns the main queue. This queue is created automatically on behalf of
 * the main thread before main() is called.
 */
DISPATCH_INLINE DISPATCH_ALWAYS_INLINE DISPATCH_CONST DISPATCH_NOTHROW
dispatch_queue_t
dispatch_get_main_queue(void)
{
    return DISPATCH_GLOBAL_OBJECT(dispatch_queue_t, _dispatch_main_q);
}

#define DISPATCH_GLOBAL_OBJECT(t, x) (&(x))

DISPATCH_CACHELINE_ALIGN
struct dispatch_queue_s _dispatch_main_q = {
    DISPATCH_GLOBAL_OBJECT_HEADER(queue_main),
#if !DISPATCH_USE_RESOLVERS
    .do_targetq = &_dispatch_root_queues[
            DISPATCH_ROOT_QUEUE_IDX_DEFAULT_QOS_OVERCOMMIT],
#endif
    .dq_state = DISPATCH_QUEUE_STATE_INIT_VALUE(1) |
            DISPATCH_QUEUE_ROLE_BASE_ANON,
    .dq_label = "com.apple.main-thread",
    .dq_atomic_flags = DQF_THREAD_BOUND | DQF_CANNOT_TRYSYNC | DQF_WIDTH(1),
    .dq_serialnum = 1,
};

這里我們發(fā)現(xiàn)通過 dispatch_get_main_queue 獲取的值就是已經(jīng)定義好的全局變量 _dispatch_main_q 抵乓。 全局工程只有一份伴挚。

CFRunloop相關(guān)代碼

CFRunloop 的創(chuàng)建

static CFRunLoopRef __CFRunLoopCreate(pthread_t t) {
    CFRunLoopRef loop = NULL;
    CFRunLoopModeRef rlm;
    uint32_t size = sizeof(struct __CFRunLoop) - sizeof(CFRuntimeBase);
    loop = (CFRunLoopRef)_CFRuntimeCreateInstance(kCFAllocatorSystemDefault, CFRunLoopGetTypeID(), size, NULL);
    if (NULL == loop) {
    return NULL;
    }
    (void)__CFRunLoopPushPerRunData(loop);
    __CFRunLoopLockInit(&loop->_lock);
    loop->_wakeUpPort = __CFPortAllocate();
    if (CFPORT_NULL == loop->_wakeUpPort) HALT;
    __CFRunLoopSetIgnoreWakeUps(loop);
    loop->_commonModes = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
    CFSetAddValue(loop->_commonModes, kCFRunLoopDefaultMode);
    loop->_commonModeItems = NULL;
    loop->_currentMode = NULL;
    loop->_modes = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
    loop->_blocks_head = NULL;
    loop->_blocks_tail = NULL;
    loop->_counterpart = NULL;
    loop->_pthread = t;
#if DEPLOYMENT_TARGET_WINDOWS
    loop->_winthread = GetCurrentThreadId();
#else
    loop->_winthread = 0;
#endif
    rlm = __CFRunLoopFindMode(loop, kCFRunLoopDefaultMode, true);
    if (NULL != rlm) __CFRunLoopModeUnlock(rlm);
    return loop;
}

通過創(chuàng)建我們知道 runloop 和tread 是一一對(duì)應(yīng)的靶衍。

接著我們看看runloop 跑起來在干么

static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode){
    uint64_t startTSR = mach_absolute_time();

    if (__CFRunLoopIsStopped(rl)) {
        __CFRunLoopUnsetStopped(rl);
    return kCFRunLoopRunStopped;
    } else if (rlm->_stopped) {
    rlm->_stopped = false;
    return kCFRunLoopRunStopped;
    }
    
    mach_port_name_t dispatchPort = MACH_PORT_NULL;
    Boolean libdispatchQSafe = pthread_main_np() && ((HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && NULL == previousMode) || (!HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && 0 == _CFGetTSD(__CFTSDKeyIsInGCDMainQ)));
    if (libdispatchQSafe && (CFRunLoopGetMain() == rl) && CFSetContainsValue(rl->_commonModes, rlm->_name)) dispatchPort = _dispatch_get_main_queue_port_4CF();
    
#if USE_DISPATCH_SOURCE_FOR_TIMERS
    mach_port_name_t modeQueuePort = MACH_PORT_NULL;
    if (rlm->_queue) {
        modeQueuePort = _dispatch_runloop_root_queue_get_port_4CF(rlm->_queue);
        if (!modeQueuePort) {
            CRASH("Unable to get port for run loop mode queue (%d)", -1);
        }
    }
#endif
    
    dispatch_source_t timeout_timer = NULL;
    struct __timeout_context *timeout_context = (struct __timeout_context *)malloc(sizeof(*timeout_context));
    if (seconds <= 0.0) { // instant timeout
        seconds = 0.0;
        timeout_context->termTSR = 0ULL;

    } else if (seconds <= TIMER_INTERVAL_LIMIT) {
    dispatch_queue_t queue = pthread_main_np() ? __CFDispatchQueueGetGenericMatchingMain() : __CFDispatchQueueGetGenericBackground();
    timeout_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
        dispatch_retain(timeout_timer);
    timeout_context->ds = timeout_timer;
    timeout_context->rl = (CFRunLoopRef)CFRetain(rl);
    timeout_context->termTSR = startTSR + __CFTimeIntervalToTSR(seconds);
    dispatch_set_context(timeout_timer, timeout_context); // source gets ownership of context
    dispatch_source_set_event_handler_f(timeout_timer, __CFRunLoopTimeout);
        dispatch_source_set_cancel_handler_f(timeout_timer, __CFRunLoopTimeoutCancel);
        
        uint64_t ns_at = (uint64_t)((__CFTSRToTimeInterval(startTSR) + seconds) * 1000000000ULL);
        ///這里超時(shí)時(shí)間是 ns_at+1 秒
        dispatch_source_set_timer(timeout_timer, dispatch_time(1, ns_at), DISPATCH_TIME_FOREVER, 1000ULL);
        dispatch_resume(timeout_timer);
    } else { // infinite timeout
        seconds = 9999999999.0;
        timeout_context->termTSR = UINT64_MAX;
    }

    Boolean didDispatchPortLastTime = true;
    int32_t retVal = 0;
    do {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
        voucher_mach_msg_state_t voucherState = VOUCHER_MACH_MSG_STATE_UNCHANGED;
        voucher_t voucherCopy = NULL;
#endif
        uint8_t msg_buffer[3 * 1024];
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
        mach_msg_header_t *msg = NULL;
        mach_port_t livePort = MACH_PORT_NULL;
#elif DEPLOYMENT_TARGET_WINDOWS
        HANDLE livePort = NULL;
        Boolean windowsMessageReceived = false;
#endif
        __CFPortSet waitSet = rlm->_portSet;
        
        __CFRunLoopUnsetIgnoreWakeUps(rl);
        
        if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
        if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
        
        ///執(zhí)行下當(dāng)前的rlm
        __CFRunLoopDoBlocks(rl, rlm);
        
        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
        if (sourceHandledThisLoop) {
            ///執(zhí)行source0
            __CFRunLoopDoBlocks(rl, rlm);
        }
        
        ///執(zhí)行了source0 并且超時(shí)了
        Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);
//        dispatchPort 主線程
        if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) {
            
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
            msg = (mach_msg_header_t *)msg_buffer;
            if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
                goto handle_msg;
            }
#elif DEPLOYMENT_TARGET_WINDOWS
            if (__CFRunLoopWaitForMultipleObjects(NULL, &dispatchPort, 0, 0, &livePort, NULL)) {
                goto handle_msg;
            }
#endif
        }
        
        didDispatchPortLastTime = false;
        ///沒有超時(shí),那么就進(jìn)入等待之前狀態(tài)
        if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
      
        ///標(biāo)記為休眠狀態(tài)
        __CFRunLoopSetSleeping(rl);
        // do not do any user callouts after this point (after notifying of sleeping)
        
        // Must push the local-to-this-activation ports in on every loop
        // iteration, as this mode could be run re-entrantly and we don't
        // want these ports to get serviced.
        
        __CFPortSetInsert(dispatchPort, waitSet);
        
        __CFRunLoopModeUnlock(rlm);
        __CFRunLoopUnlock(rl);
        
        CFAbsoluteTime sleepStart = poll ? 0.0 : CFAbsoluteTimeGetCurrent();
        
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
#if USE_DISPATCH_SOURCE_FOR_TIMERS
        do {
            ///等待被喚醒
            if (kCFUseCollectableAllocator) {
                // objc_clear_stack(0);
                // <rdar://problem/16393959>
                memset(msg_buffer, 0, sizeof(msg_buffer));
            }
            msg = (mach_msg_header_t *)msg_buffer;
            
            ///無限暫停 相當(dāng)于卡主了
            __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
           
            
            if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
//                執(zhí)行下這個(gè)隊(duì)列
                // 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);
#else
        if (kCFUseCollectableAllocator) {
            // objc_clear_stack(0);
            // <rdar://problem/16393959>
            memset(msg_buffer, 0, sizeof(msg_buffer));
        }
        msg = (mach_msg_header_t *)msg_buffer;
        __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
#endif
        
        
#elif DEPLOYMENT_TARGET_WINDOWS
        // Here, use the app-supplied message queue mask. They will set this if they are interested in having this run loop receive windows messages.
        __CFRunLoopWaitForMultipleObjects(waitSet, NULL, poll ? 0 : TIMEOUT_INFINITY, rlm->_msgQMask, &livePort, &windowsMessageReceived);
#endif
        
        __CFRunLoopLock(rl);
        __CFRunLoopModeLock(rlm);
        
        rl->_sleepTime += (poll ? 0.0 : (CFAbsoluteTimeGetCurrent() - sleepStart));
        
        // Must remove the local-to-this-activation ports in on every loop
        // iteration, as this mode could be run re-entrantly and we don't
        // want these ports to get serviced. Also, we don't want them left
        // in there if this function returns.
        
        __CFPortSetRemove(dispatchPort, waitSet);
        
        __CFRunLoopSetIgnoreWakeUps(rl);
        
        // user callouts now OK again
        ///標(biāo)記被喚醒
        __CFRunLoopUnsetSleeping(rl);
        if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
        
        ///看看到底喚醒的是啥
        
    handle_msg:;
        __CFRunLoopSetIgnoreWakeUps(rl);
        
#if DEPLOYMENT_TARGET_WINDOWS
        if (windowsMessageReceived) {
            // These Win32 APIs cause a callout, so make sure we're unlocked first and relocked after
            __CFRunLoopModeUnlock(rlm);
            __CFRunLoopUnlock(rl);
            
            if (rlm->_msgPump) {
                rlm->_msgPump();
            } else {
                MSG msg;
                if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD)) {
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
            }
            
            __CFRunLoopLock(rl);
            __CFRunLoopModeLock(rlm);
            sourceHandledThisLoop = true;
            
            // To prevent starvation of sources other than the message queue, we check again to see if any other sources need to be serviced
            // Use 0 for the mask so windows messages are ignored this time. Also use 0 for the timeout, because we're just checking to see if the things are signalled right now -- we will wait on them again later.
            // NOTE: Ignore the dispatch source (it's not in the wait set anymore) and also don't run the observers here since we are polling.
            __CFRunLoopSetSleeping(rl);
            __CFRunLoopModeUnlock(rlm);
            __CFRunLoopUnlock(rl);
            
            __CFRunLoopWaitForMultipleObjects(waitSet, NULL, 0, 0, &livePort, NULL);
            
            __CFRunLoopLock(rl);
            __CFRunLoopModeLock(rlm);
            __CFRunLoopUnsetSleeping(rl);
            // If we have a new live port then it will be handled below as normal
        }
        
        
#endif
        if (MACH_PORT_NULL == livePort) {
            CFRUNLOOP_WAKEUP_FOR_NOTHING();
            // handle nothing
        } else if (livePort == rl->_wakeUpPort) {
            CFRUNLOOP_WAKEUP_FOR_WAKEUP();
            // do nothing on Mac OS
#if DEPLOYMENT_TARGET_WINDOWS
            // Always reset the wake up port, or risk spinning forever
            ResetEvent(rl->_wakeUpPort);
#endif
        }
#if USE_DISPATCH_SOURCE_FOR_TIMERS
        else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
            ///處理timer
            CFRUNLOOP_WAKEUP_FOR_TIMER();
            if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
                // Re-arm the next timer, because we apparently fired early
                __CFArmNextTimerInMode(rlm, rl);
            }
        }
#endif
#if USE_MK_TIMER_TOO
        else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) {
            CFRUNLOOP_WAKEUP_FOR_TIMER();
            // On Windows, we have observed an issue where the timer port is set before the time which we requested it to be set. For example, we set the fire time to be TSR 167646765860, but it is actually observed firing at TSR 167646764145, which is 1715 ticks early. The result is that, when __CFRunLoopDoTimers checks to see if any of the run loop timers should be firing, it appears to be 'too early' for the next timer, and no timers are handled.
            // In this case, the timer port has been automatically reset (since it was returned from MsgWaitForMultipleObjectsEx), and if we do not re-arm it, then no timers will ever be serviced again unless something adjusts the timer list (e.g. adding or removing timers). The fix for the issue is to reset the timer here if CFRunLoopDoTimers did not handle a timer itself. 9308754
            if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
                // Re-arm the next timer
                __CFArmNextTimerInMode(rlm, rl);
            }
        }
#endif
        else if (livePort == dispatchPort) {
            CFRUNLOOP_WAKEUP_FOR_DISPATCH();
            __CFRunLoopModeUnlock(rlm);
            __CFRunLoopUnlock(rl);
            _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)6, NULL);
#if DEPLOYMENT_TARGET_WINDOWS
            void *msg = 0;
#endif
            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
            _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)0, NULL);
            __CFRunLoopLock(rl);
            __CFRunLoopModeLock(rlm);
            sourceHandledThisLoop = true;
            didDispatchPortLastTime = true;
        } else {
            CFRUNLOOP_WAKEUP_FOR_SOURCE();
            
            // If we received a voucher from this mach_msg, then put a copy of the new voucher into TSD. CFMachPortBoost will look in the TSD for the voucher. By using the value in the TSD we tie the CFMachPortBoost to this received mach_msg explicitly without a chance for anything in between the two pieces of code to set the voucher again.
            voucher_t previousVoucher = _CFSetTSD(__CFTSDKeyMachMessageHasVoucher, (void *)voucherCopy, os_release);
            
            // Despite the name, this works for windows handles as well
            CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);
            if (rls) {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
                mach_msg_header_t *reply = NULL;
                sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;
                if (NULL != reply) {
                    (void)mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
                    CFAllocatorDeallocate(kCFAllocatorSystemDefault, reply);
                }
#elif DEPLOYMENT_TARGET_WINDOWS
                sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls) || sourceHandledThisLoop;
#endif
            }
            
            // Restore the previous voucher
            _CFSetTSD(__CFTSDKeyMachMessageHasVoucher, previousVoucher, os_release);
            
        }
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
        if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
#endif
        
        __CFRunLoopDoBlocks(rl, rlm);
        
        if (sourceHandledThisLoop && stopAfterHandle) {
            retVal = kCFRunLoopRunHandledSource;
        } else if (timeout_context->termTSR < mach_absolute_time()) {
            retVal = kCFRunLoopRunTimedOut;
        } else if (__CFRunLoopIsStopped(rl)) {
            __CFRunLoopUnsetStopped(rl);
            retVal = kCFRunLoopRunStopped;
        } else if (rlm->_stopped) {
            rlm->_stopped = false;
            retVal = kCFRunLoopRunStopped;
        } else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
            retVal = kCFRunLoopRunFinished;
        }
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
        voucher_mach_msg_revert(voucherState);
        os_release(voucherCopy);
#endif
        
    } while (0 == retVal);

    if (timeout_timer) {
        dispatch_source_cancel(timeout_timer);
        dispatch_release(timeout_timer);
    } else {
        free(timeout_context);
    }

    return retVal;
}

上面代碼的邏輯圖概況成這張圖


runloop run的邏輯圖

從**static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) **中我們找到這么一段代碼

#define HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY 0

    mach_port_name_t dispatchPort = MACH_PORT_NULL;
    Boolean libdispatchQSafe = pthread_main_np() && ((HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && NULL == previousMode) || (!HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && 0 == _CFGetTSD(__CFTSDKeyIsInGCDMainQ)));
    if (libdispatchQSafe && (CFRunLoopGetMain() == rl) && CFSetContainsValue(rl->_commonModes, rlm->_name)) dispatchPort = _dispatch_get_main_queue_port_4CF();

#define _dispatch_get_main_queue_port_4CF _dispatch_get_main_queue_handle_4CF

dispatch_runloop_handle_t
_dispatch_get_main_queue_handle_4CF(void)
{
    dispatch_queue_t dq = &_dispatch_main_q;
    dispatch_once_f(&_dispatch_main_q_handle_pred, dq,
            _dispatch_runloop_queue_handle_init);
    return _dispatch_runloop_queue_get_handle(dq);
}

typedef mach_port_t dispatch_runloop_handle_t;

從上面我們能看出

1 dispatchPort = _dispatch_get_main_queue_port_4CF()茎芋;
2 _dispatch_get_main_queue_port_4CF = _dispatch_get_main_queue_handle_4CF
3 而_dispatch_get_main_queue_handle_4CF 中的返回值是從_dispatch_main_q 中獲取的
4 _dispatch_main_q 是什么呢颅眶?就是我們上面通過dispatch_get_main_queue(void) 函數(shù)獲取的。

這里主線程跑起來了败徊,并且獲取了main queue的port。獲取了port掏缎,我們看看再干了啥事情皱蹦。截取片段


           msg = (mach_msg_header_t *)msg_buffer;
            if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
                goto handle_msg;
            }

 else if (livePort == dispatchPort) {
            CFRUNLOOP_WAKEUP_FOR_DISPATCH();
            __CFRunLoopModeUnlock(rlm);
            __CFRunLoopUnlock(rl);
            _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)6, NULL);
#if DEPLOYMENT_TARGET_WINDOWS
            void *msg = 0;
#endif
            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
            _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)0, NULL);
            __CFRunLoopLock(rl);
            __CFRunLoopModeLock(rlm);
            sourceHandledThisLoop = true;
            didDispatchPortLastTime = true;
        }

1 __CFRunLoopServiceMachPort 是監(jiān)聽端口號(hào)
這里我們發(fā)現(xiàn)當(dāng)端口號(hào)激活了之后,會(huì)有個(gè)檢測(cè)激活的端口號(hào)是否和dispatchPort(主隊(duì)列的端口號(hào)是否相同)眷蜈,那么主線程的runloop就處理這個(gè)端口號(hào)事件了沪哺。

總結(jié)下

1 沒有啟動(dòng)主線程的runloop 的時(shí)候,主線程和其他線程沒有什么區(qū)別酌儒,主隊(duì)列和其他隊(duì)列也沒啥區(qū)別
2 啟動(dòng)主線程的runloop的時(shí)候辜妓,主線程通過port 監(jiān)聽主隊(duì)列的block,而其他線程啟動(dòng)runloop 沒有此功能忌怎。

這里有段話也比較重要貼下

線程切換時(shí)的寄存器復(fù)用
我們的代碼并不是只在單線程中執(zhí)行籍滴,而是可能在多個(gè)線程中執(zhí)行。那么這里你就可能會(huì)產(chǎn)生一個(gè)疑問榴啸?既然進(jìn)程中有多個(gè)線程在并行執(zhí)行孽惰,而CPU中的寄存器又只有那么一套,如果不加處理豈不會(huì)產(chǎn)生數(shù)據(jù)錯(cuò)亂的場(chǎng)景鸥印?答案是否定的勋功。我們知道線程是一個(gè)進(jìn)程中的執(zhí)行單元,每個(gè)線程的調(diào)度執(zhí)行其實(shí)都是通過操作系統(tǒng)來完成库说。也就是說哪個(gè)線程占有CPU執(zhí)行以及執(zhí)行多久都是由操作系統(tǒng)控制的狂鞋。具體的實(shí)現(xiàn)是每創(chuàng)建一個(gè)線程時(shí)都會(huì)為這線程創(chuàng)建一個(gè)數(shù)據(jù)結(jié)構(gòu)來保存這個(gè)線程的信息,我們稱這個(gè)數(shù)據(jù)結(jié)構(gòu)為線程上下文潜的,每個(gè)線程的上下文中有一部分?jǐn)?shù)據(jù)是用來保存當(dāng)前所有寄存器的副本骚揍。每當(dāng)操作系統(tǒng)暫停一個(gè)線程時(shí),就會(huì)將CPU中的所有寄存器的當(dāng)前內(nèi)容都保存到線程上下文數(shù)據(jù)結(jié)構(gòu)中啰挪。而操作系統(tǒng)要讓另外一個(gè)線程執(zhí)行時(shí)則將要執(zhí)行的線程的上下文中保存的所有寄存器的內(nèi)容再寫回到CPU中疏咐,并將要運(yùn)行的線程中上次保存暫停的指令也賦值給CPU的指令寄存器,并讓新線程再次執(zhí)行脐供』肴可以看出操作系統(tǒng)正是通過這種機(jī)制保證了即使是多線程運(yùn)行時(shí)也不會(huì)導(dǎo)致寄存器的內(nèi)容發(fā)生錯(cuò)亂的問題。因?yàn)槊慨?dāng)線程切換時(shí)操作系統(tǒng)都幫它們將數(shù)據(jù)處理好了政己。下面的部分線程上下文結(jié)構(gòu)正是指定了所有寄存器信息的部分:

//這個(gè)結(jié)構(gòu)是linux在arm32CPU上的線程上下文結(jié)構(gòu)酌壕,代碼來自于:http://elixir.free-electrons.com/linux/latest/source/arch/arm/include/asm/thread_info.h  
//這里并沒有保存所有的寄存器掏愁,是因?yàn)锳BI中定義linux在arm上運(yùn)行時(shí)所使用的寄存器并不是全體寄存器,所以只需要保存規(guī)定的寄存器的內(nèi)容即可卵牍。這里并不是所有的CPU所保存的內(nèi)容都是一致的果港,保存的內(nèi)容會(huì)根據(jù)CPU架構(gòu)的差異而不同。
//因?yàn)閕OS的內(nèi)核并未開源所以無法得到iOS定義的線程上下文結(jié)構(gòu)糊昙。

//線程切換時(shí)要保存的CPU寄存器辛掠,
struct cpu_context_save {
    __u32   r4;
    __u32   r5;
    __u32   r6;
    __u32   r7;
    __u32   r8;
    __u32   r9;
    __u32   sl;
    __u32   fp;
    __u32   sp;
    __u32   pc;
    __u32   extra[2];       /* Xscale 'acc' register, etc */
};

//線程上下文結(jié)構(gòu)
struct thread_info {
    unsigned long       flags;      /* low level flags */
    int         preempt_count;  /* 0 => preemptable, <0 => bug */
    mm_segment_t        addr_limit; /* address limit */
    struct task_struct  *task;      /* main task structure */
    __u32           cpu;        /* cpu */
    __u32           cpu_domain; /* cpu domain */
    struct cpu_context_save cpu_context;    /* cpu context */
    __u32           syscall;    /* syscall number */
    __u8            used_cp[16];    /* thread used copro */
    unsigned long       tp_value[2];    /* TLS registers */
#ifdef CONFIG_CRUNCH
    struct crunch_state crunchstate;
#endif
    union fp_state      fpstate __attribute__((aligned(8)));  /*浮點(diǎn)寄存器*/
    union vfp_state     vfpstate;  /*向量浮點(diǎn)寄存器*/
#ifdef CONFIG_ARM_THUMBEE
    unsigned long       thumbee_state;  /* ThumbEE Handler Base register */
#endif
};

線程切換

libdispatch 源碼地址
cfrunloop源碼地址
cfrunloop源碼下載地址
奇怪的GCD

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市释牺,隨后出現(xiàn)的幾起案子萝衩,更是在濱河造成了極大的恐慌,老刑警劉巖没咙,帶你破解...
    沈念sama閱讀 216,744評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件猩谊,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡祭刚,警方通過查閱死者的電腦和手機(jī)牌捷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來涡驮,“玉大人暗甥,你說我怎么就攤上這事∽酵保” “怎么了淋袖?”我有些...
    開封第一講書人閱讀 163,105評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)锯梁。 經(jīng)常有香客問我即碗,道長(zhǎng),這世上最難降的妖魔是什么陌凳? 我笑而不...
    開封第一講書人閱讀 58,242評(píng)論 1 292
  • 正文 為了忘掉前任剥懒,我火速辦了婚禮,結(jié)果婚禮上合敦,老公的妹妹穿的比我還像新娘初橘。我一直安慰自己,他們只是感情好充岛,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評(píng)論 6 389
  • 文/花漫 我一把揭開白布保檐。 她就那樣靜靜地躺著,像睡著了一般崔梗。 火紅的嫁衣襯著肌膚如雪夜只。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,215評(píng)論 1 299
  • 那天蒜魄,我揣著相機(jī)與錄音扔亥,去河邊找鬼场躯。 笑死,一個(gè)胖子當(dāng)著我的面吹牛旅挤,可吹牛的內(nèi)容都是我干的踢关。 我是一名探鬼主播,決...
    沈念sama閱讀 40,096評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼粘茄,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼签舞!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起柒瓣,我...
    開封第一講書人閱讀 38,939評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤儒搭,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后嘹朗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體师妙,經(jīng)...
    沈念sama閱讀 45,354評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡诵肛,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評(píng)論 2 333
  • 正文 我和宋清朗相戀三年屹培,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片怔檩。...
    茶點(diǎn)故事閱讀 39,745評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡褪秀,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出薛训,到底是詐尸還是另有隱情媒吗,我是刑警寧澤,帶...
    沈念sama閱讀 35,448評(píng)論 5 344
  • 正文 年R本政府宣布乙埃,位于F島的核電站闸英,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏介袜。R本人自食惡果不足惜甫何,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望遇伞。 院中可真熱鬧辙喂,春花似錦、人聲如沸鸠珠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽渐排。三九已至炬太,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間驯耻,已是汗流浹背娄琉。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評(píng)論 1 269
  • 我被黑心中介騙來泰國打工次乓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人孽水。 一個(gè)月前我還...
    沈念sama閱讀 47,776評(píng)論 2 369
  • 正文 我出身青樓票腰,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親女气。 傳聞我的和親對(duì)象是個(gè)殘疾皇子杏慰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容