使用版本:
libdispatch-1173.40.5.tar
使用部分网上解析作参考, 但因为版本问题, 很多不一样, 这里主要针对当前使用的源码
网上有篇叫, 感觉全网都是抄他的, 然而里面除了列出源码啥都没讲
主要解析以下这些常用的结构体, 源码中涉及的结构体太多了, 而且 极绕!, 暂时不深究其他
dispatch_object_sdispatch_queue_sdispatch_queue_attr_tdispatch_group_sdispatch_source_sdispatch_semaphore_s
这些结构体内部都会涉及一个宏, 用于描述其 isa, 我归纳到这里其他: GCD 中 isa 中有什么
基础知识
-
C 中的宏
#,##的含义:
前者作用是转为字符串, 后者作用是将东西黏在一起, 具体参考见: C语言宏定义中#和##的作用
#:#define str(s) #s=>str(hello)转换为"hello"
##:#define v(a,b,c,d) 0xa##b##c##d=>v(CF,AB,B0,BC)准换为0xCFABB0BC -
C 中使用宏来做代码拼接, 在用法上可以模拟出我们理解的继承概念(子类拥有父类定义过的字段), 在宏定义时, 只要将
struct的字段用宏一层层定义即可. 下层定义最基础字段, 上层引用了这个宏, 就会带进来这些字段. 不同的struct内部包含相同的宏,就会有同样的字段
(当然, 和继承不一样的是, 宏的写法可自由了, 而用宏不用继承的原因,是继承会带来额外的性能消耗?) -
GCD 源码中数据结构极绕!, 涉及大量宏的使用, 每一个基本的
struct都是由宏一层层拼接的, 这里以dispatch_object_s举例, 描述整个追溯过程.
下面提到的几个宏, 在源码中会经常出现, 使用相同宏的 struct 证明其中部分数据结构类似
这里涉及几个宏, 我写在这里 其他: 数据结构中使用的宏
(ps. 这里面的宏包含了之后结构体都会有的通用字段
下面拼接一次, 后面讲结构体都直接放上拼接后的内容, 不再一步步拼接
1.定义的数据结构struct dispatch_object_s { _DISPATCH_OBJECT_HEADER(object); };2.拼接
_DISPATCH_OBJECT_HEADER#define _DISPATCH_OBJECT_HEADER(x) \ struct _os_object_s _as_os_obj[0]; \ OS_OBJECT_STRUCT_HEADER(dispatch_##x); \ struct dispatch_##x##_s *volatile do_next; \ struct dispatch_queue_s *do_targetq; \ void *do_ctxt; \ void *do_finalizer //展开后: struct dispatch_object_s { struct _os_object_s _as_os_obj[0]; OS_OBJECT_STRUCT_HEADER(dispatch_object); struct dispatch_object_s *volatile do_next; struct dispatch_queue_s *do_targetq; void *do_ctxt; void *do_finalizer };3.拼接
OS_OBJECT_STRUCT_HEADER#define OS_OBJECT_STRUCT_HEADER(x) \ _OS_OBJECT_HEADER(\ const struct x##_vtable_s *do_vtable, \ do_ref_cnt, \ do_xref_cnt) #endif展开后:
// ↓ dispatch_object_s struct dispatch_object_s { struct _os_object_s _as_os_obj[0]; _OS_OBJECT_HEADER( const struct dispatch_object_vtable_s *do_vtable, \ do_ref_cnt, do_xref_cnt ) struct dispatch_object_s *volatile do_next; struct dispatch_queue_s *do_targetq; void *do_ctxt; void *do_finalizer };4.展开
_OS_OBJECT_HEADER#define _OS_OBJECT_HEADER(isa, ref_cnt, xref_cnt) \ isa; /* must be pointer-sized */ \ int volatile ref_cnt; \ int volatile xref_cnt展开后: (有的文章中多出个 isa, 我自己推下来
do_vtable就是这个 isa, 实际看其他部分源码, 也确实如此.)struct dispatch_object_s { struct _os_object_s _as_os_obj[0]; const struct dispatch_object_vtable_s *do_vtable int volatile ref_cnt; int volatile xref_cnt struct dispatch_object_s *volatile do_next; struct dispatch_queue_s *do_targetq; void *do_ctxt; void *do_finalizer };
dispatch_object_t
typedef union {
struct _os_object_s *_os_obj;
struct dispatch_object_s *_do; //常用的 GCD 结构体
struct dispatch_queue_s *_dq; // 队列
struct dispatch_queue_attr_s *_dqa; // 队列属性
struct dispatch_group_s *_dg; // group
struct dispatch_source_s *_ds; //source
struct dispatch_channel_s *_dch;
struct dispatch_mach_s *_dm; //mach 内核
struct dispatch_mach_msg_s *_dmsg; // mach 内核相关
struct dispatch_semaphore_s *_dsema; //信号量
struct dispatch_data_s *_ddata;
struct dispatch_io_s *_dchannel;
} dispatch_object_t DISPATCH_TRANSPARENT_UNION;
可以理解为 dispatch_object_t, 是为了解决语法中类型问题, 用一个大的结构体, 封装了GCD 中常用的基本类型
(类似于函数接口或者返回值用父类,可以传入子类或返回任何子类的操作)
其内部的 dispatch_object_s, dispatch_queue_s, dispatch_source_s, dispatch_semaphore_s
就是我们使用 GCD 常见的那几个
后面会发现, 这些基本的数据结构, 都包含一系列通用字段, 这篇文章里, 我对他们的 isa 做了解析 其他: GCD 中 isa 中有什么
而通用的宏提取到这篇文章中 其他: 数据结构中使用的宏, 之后也不会再细讲
DISPATCH_OBJECT_HEADER -> _DISPATCH_OBJECT_HEADER -> OS_OBJECT_STRUCT_HEADER -> _OS_OBJECT_HEADER
(简单的理解(不完全准确), 就是从父类继承的意思)
union 在 runtime, runloop 都介绍过,
dispatch_object_t结构体
只包含内部任意的其中一个的字段, 并且不管是哪个字段,地址都是同一个.内核相关的内容见之前我在 runloop 中提到的 其他: Mach 是什么, 不做过深探究
dispatch_object_s
探究过程已经在上面作为例子了
主要是宏 _DISPATCH_OBJECT_HEADER, 提取到这篇文章中 其他: 数据结构中使用的宏
后面不再列出
_as_os_obj:暂时未知do_vtable: 以宏解析过程的名字看,是 isa, isa 在runtime, runloop 源码都有使用,一般指向其存放了信息的实体(类,元类)do_ref_cnt: 内部引用计数do_xref_cnt: 外部引用计数do_next: 很明显的链表节点do_targetq: 执行线程do_ctxt: 上下文 (望名猜意, 未求证)do_finalizer: 析构 (望名猜意, 未求证)struct dispatch_object_s { struct _os_object_s _as_os_obj[0]; const struct dispatch_object_vtable_s *do_vtable; int volatile do_ref_cnt; int volatile do_xref_cnt; struct dispatch_object_s *volatile do_next; struct dispatch_queue_s *do_targetq; void *do_ctxt; void *do_finalizer; };
dispatch_queue_s: GCD 调用线程必用的dispatch_queue_t
调用 GCD 必备的指针 dispatch_queue_t 的结构体类型,就是他
DISPATCH_DECL(dispatch_queue);
#define DISPATCH_DECL(name) typedef struct name##_s *name##_t
typedef struct dispatch_queue_s *dispatch_queue_t;
涉及的宏 DISPATCH_QUEUE_CLASS_HEADER -> _DISPATCH_QUEUE_CLASS_HEADER -> DISPATCH_OBJECT_HEADER -> _DISPATCH_OBJECT_HEADER -> OS_OBJECT_STRUCT_HEADER -> _OS_OBJECT_HEADER
从DISPATCH_OBJECT_HEADER开始写在 其他: 数据结构中使用的宏, 包含 isa, 链表结构等不再细讲, 和上面的dispatch_object_s除了一个字段外,基本一样
do_ctxt: 在我追踪 root_queue 时发现,存的就是线程池, 其宗旨关联到dispatch_pthread_root_queue_context_s
内部包含了一个线程数组 pthread_attr_t (也可能不是pthread, 由宏控制的,宏在 CMake 中又由别的宏控制)
DISPATCH_QUEUE_CLASS_HEADER, 多出以下字段:
dq_serialnum: 源码中初始化时, 会创建很多的queue, 然后给这些 queue 指定序号,放在这个字段
init.cdq_label: 创建线程时的标识符dq_width: 线程并发数__dq_opaque2: ❓未知, (只见定义, 源码中没看到任何用的地方)dq_priority: 优先级- ❓剩余字段用处未明晰
unsigned long dq_serialnum; \ const char *dq_label; \ DISPATCH_UNION_LE(uint32_t volatile dq_atomic_flags, \ const uint16_t dq_width, \ const uint16_t __dq_opaque2 \ ); \ dispatch_priority_t dq_priority; \ union { \ struct dispatch_queue_specific_head_s *dq_specific_head; \ struct dispatch_source_refs_s *ds_refs; \ struct dispatch_timer_source_refs_s *ds_timer_refs; \ struct dispatch_mach_recv_refs_s *dm_recv_refs; \ struct dispatch_channel_callbacks_s const *dch_callbacks; \ }; \ int volatile dq_sref_cnt - 最终展开如下
struct dispatch_queue_s { struct dispatch_object_s _as_do[0]; struct _os_object_s _as_os_obj[0]; const struct dispatch_queue_vtable_s *do_vtable, int volatile do_ref_cnt, int volatile do_xref_cnt struct dispatch_queue_s *volatile do_next; struct dispatch_queue_s *do_targetq; void *do_ctxt; void *do_finalizer DISPATCH_UNION_LE(uint64_t volatile dq_state, dispatch_lock dq_state_lock, uint32_t dq_state_bits ) /* LP64 global queue cacheline boundary */ unsigned long dq_serialnum; const char *dq_label; DISPATCH_UNION_LE(uint32_t volatile dq_atomic_flags, const uint16_t dq_width, const uint16_t __dq_opaque2 ); /* 32bit hole on LP64 */ } DISPATCH_ATOMIC64_ALIGN;
dispatch_queue_attr_s: dispatch_queue_create的入参
dispatch_queue_create 的其中一个入参, dispatch_queue_attr_t 与他的关系
DISPATCH_DECL(dispatch_queue_attr);
#define DISPATCH_DECL(name) typedef struct name##_s *name##_t
typedef struct dispatch_queue_attr_s * dispatch_queue_attr_t;
具体结构和之前一样是通用的OS_OBJECT_STRUCT_HEADER , 归纳在这 其他: 数据结构使用的宏(都有的字段)
struct dispatch_queue_attr_s {
OS_OBJECT_STRUCT_HEADER(dispatch_queue_attr);
};
dispatch_group_s
常用的 dispatch_group_t 的结构体
DISPATCH_DECL(dispatch_group);
#define DISPATCH_DECL(name) typedef struct name##_s *name##_t
typedef struct dispatch_group_s * dispatch_group_t;
DISPATCH_OBJECT_HEADER其他: 数据结构使用的宏(都有的字段) 不再细讲
可以看到多出来的字段明显是个链表结构:
dg_notify_head: 头结点dg_notify_tail: 尾结点
联想我们的使用流程, 不难得出, 这是dispatch_group_s在执行完后, 调用 notify 时的链表dg_state: 字面意思描述状态用的, 用于在内部函数中判断用dg_bits: 对应使用的函数是dispatch_group_enter,dispatch_group_leave, 看的出来是防止异常的enter和leave不匹配而设置的变量struct dispatch_group_s { DISPATCH_OBJECT_HEADER(group); DISPATCH_UNION_LE(uint64_t volatile dg_state, uint32_t dg_bits, uint32_t dg_gen ) DISPATCH_ATOMIC64_ALIGN; struct dispatch_continuation_s *volatile dg_notify_head; struct dispatch_continuation_s *volatile dg_notify_tail; };
dispatch_semaphore_s: 信号量
同上 DISPATCH_OBJECT_HEADER 在 其他: 数据结构使用的宏(都有的字段) 中
dsema_value: 使用信号量时创建的 量 值就存在这, 具体调度在dispatch_semaphore_create中dsema_orig: 同上, 与上面不同的是, 上面记录当前值, 这个记录原始值dsema_sema: 暂时没从函数中看出来是干嘛的, 有文章提到与semaphore_create相关, 从名字看也确实应该相关, 但我从源码中看不出来struct dispatch_semaphore_s { DISPATCH_OBJECT_HEADER(semaphore); long volatile dsema_value; long dsema_orig; _dispatch_sema4_t dsema_sema; };dispatch_semaphore_t dispatch_semaphore_create(long value) { //省略大部分 dsema->dsema_value = value; _dispatch_sema4_init(&dsema->dsema_sema, _DSEMA4_POLICY_FIFO); dsema->dsema_orig = value; }
dispatch_continuation_s: 所有执行体的封装
在源码实现过程中会发现很多很多地方用到这个结构体, 这里先直接给出结论, 他就是 GCD dispatch_async dispatch_group_notify 等操作传入的 block 的封装.
其中涉及到的宏 DISPATCH_CONTINUATION_HEADER 有很多总定义,这里只取 __LP64__下的
#define DISPATCH_CONTINUATION_HEADER(x) \
union { \
const void *do_vtable; \
uintptr_t dc_flags; \
}; \
union { \
pthread_priority_t dc_priority; \
int dc_cache_cnt; \
uintptr_t dc_pad; \
}; \
struct dispatch_##x##_s *volatile do_next; \
struct voucher_s *dc_voucher; \
dispatch_function_t dc_func; \
void *dc_ctxt; \
void *dc_data; \
void *dc_other
最终展开为
dc_func: 根据很多函数中查找的结果, 是最终执行函数的指针_dispatch_block_async_invoke2- 有几个字段其他结构体中也有
do_vtable,do_next,dc_ctxt - ❓其他内容源码中暂时推断不出作用
dispatch_function_t是个宏typedef void (*dispatch_function_t)(void *_Nullable);
很单纯的函数指针typedef struct dispatch_continuation_s { union { const void *do_vtable; uintptr_t dc_flags; }; union { pthread_priority_t dc_priority; int dc_cache_cnt; uintptr_t dc_pad; }; struct dispatch_continuation_s *volatile do_next; struct voucher_s *dc_voucher; dispatch_function_t dc_func; void *dc_ctxt; void *dc_data; void *dc_other } *dispatch_continuation_t;
dispatch_queue_static_s 用于描述主线程的结构体
惊喜吧, 主线程和普通线程在底层是不一样的结构体
struct dispatch_queue_static_s {
struct dispatch_lane_s _as_dl[0]; \
DISPATCH_LANE_CLASS_HEADER(lane);
} DISPATCH_CACHELINE_ALIGN;
与dispatch_queue_s 一样拥有 DISPATCH_QUEUE_CLASS_HEADER
多出了 dq_items_head
#define DISPATCH_LANE_CLASS_HEADER(x) \
struct dispatch_queue_s _as_dq[0]; \
DISPATCH_QUEUE_CLASS_HEADER(x, \
struct dispatch_object_s *volatile dq_items_tail); \
dispatch_unfair_lock_s dq_sidelock; \
struct dispatch_object_s *volatile dq_items_head; \
uint32_t dq_side_suspend_cnt