dispatch source 和 runLoop source 都是用来监听事件的,能够建立不一样类型的 dispatch source 和 runLoop source 。dispatch source 监听到事件产生时,会将 event handler 添加到目标 queue。runLoop source 须要先按照某种模式加入到指定线程的 runLoop 中。dispatch source 和 runLoop source 都是异步处理模式,只要建立、设置好,就能够在相应的 handler 中监听到相应事件的产生。node
GCD 中除了主要的 Dispatch Queue 外, 还有不太引人注目的Dispatch Source(信号源)。它是BSD系内核惯有功能 kqueue 的包装。nginx
lsof 是 list open files 的简称,它的做用主要是列出系统中打开的文件。乍看起来,这是个功能很是简单,使用场景很少的命令,不过是ls的另个版本。lsof 能够知道用户和进程操做了哪些文件,也能够查看系统中网络的使用状况,以及设备的信息。web
sudo lsof -p 5858
sudo lsof -c nginx
sudo lsof -i
sudo lsof -i TCP
sudo lsof -i :80
名称 | 说明 | 内容 |
---|---|---|
PID | 进程ID | n/a |
FD(file descriptor) | 文件描述符 | wd:当前工做目录 |
TYPE | n/a | IPv4: IPv4 socket |
DEVICE | 设备号码 | n/a |
SIZE/OFF | 文件大小/偏移量 | n/a |
NODE | inode编号,包含文件的元信息 | 文件的字节数 |
NAME | 打开的文件(即file descriptor所指向的文件) | n/a |
CPU单核在同一时刻只能作一件事情,一种解决办法是对 CPU 进行时分复用(多个事件流将CPU 切割成多个时间片,不一样事件流的时间片交替进行)。网络
在计算机系统中,咱们用线程或者进程来表示一条执行流,经过不一样的线程或进程在操做系统内部的调度,来作到对 CPU 处理的时分复用。这样多个事件流就能够并发进行,不须要一个等待另外一个过久,在用户看起来他们彷佛就是并行在作同样。多线程
可是凡事都是有成本的。线程/进程也同样,有如下几个方面:
线程/进程建立成本;
CPU切换不一样线程/进程成本Context Switch;
多线程的资源竞争;并发
有没有一种能够在单线程/进程中处理多个事件流的方法呢? 这就是“IO多路复用”。框架
什么是“IO多路复用”?简单来讲就是经过单线程或单进程同时监测若干个文件描述符是否能够执行IO操做的能力。经过把多个I/O的阻塞复用到同一个的阻塞上,从而使得系统在单线程或单进程的状况下能够同时处理多个客户端请求。
这样在处理1000个链接时,只须要1个线程或进程监控就绪状态,对就绪的每一个链接开一个线程或进程处理就能够了。这样须要的线程或进程数大大减小,减小了内存开销和上下文切换的CPU开销。
内核实现I/O多路复用,原理就是传入多个文件描述符,若是有一个文件描述符就绪,则返回,不然阻塞直到超时。获得就绪状态后进行真正的操做能够在同一个线程或进程里执行,也能够启动线程或进程来执行(好比使用线程池)。有以下几种函数:
Dispatch Source 也使用在了 Core Foundation 框架的用于异步网络的 CFSocket 中。由于Foundation 框架的异步网络API 是经过 CFSocket 实的,因此可享受到仅使用 Foundation 框架的 Dispatch Source 带来的好处。
GCD提供了一系列的 dispatch source 用来充当监听底层系统对象:
处于活动状态时的接口,当监听到事件产生的时候,dispatch source 会自动的将事件触发的回调Block派发到指定的dispatch queue 执行。
/* @function dispatch_source_create * @discussion dispatch source不可重入.当dispatch source被挂起或者”event handler block“正在执行,这时候“dispatch source”接收到的多个事件会被合并,而且在“dispatch source”恢复或者正在执行的“event handler block”已经执行完毕以后,再来处理合并以后的事。 * * “dispatch source”建立的时候是处于“未被激活”的状态。 * 当建立一个“dispatch source”时并配置好了全部须要的属性(例如handler、context等),要想激活“dispatch source”,能够经过调用“dispatch_activate()”来开始接受事件。 * * 在“dispatch source”未被激活前,能够调用“dispatch_set_target_queue()”来设置目标队列,可是一旦被激活以后,就不能再设置。 * * 处于向后兼容,对于未激活和未挂起的“dispatch source”调用“dispatch_resume()”和调用“dispatch_activate()”有想相同做用,固然更好的激活方式是使用”dispatch_activate()“。 * * @param type * 声明“dispatch source”类型,必须是定义的“dispatch_source_type_t”常量中的一个。 * * @param handle * 要监听的系统底层对象的句柄(标志符),要监听进程,须要传入进程的ID。 * * @param mask * 指定“dispatch_source”要监听底层对象的类型。 * * @param queue * 指定“events handler block”提交到的目标queue。 * 若是目标queue是“DISPATCH_TARGET_QUEUE_DEFAULT”,"events handler block"将被提交默认的优先级的全局queue上。 * * @result * 建立好的“dispatch_source”,若是是NULL,则传入了非法参数。 */ dispatch_source_t dispatch_source_create(dispatch_source_type_t type, uintptr_t handle, unsigned long mask, dispatch_queue_t Nullable queue)
typedef const struct dispatch_source_type_t *dispatch_source_type_t;
名称 | 说明 | dispatch_source_get_handle | dispatch_source_get_mask |
---|---|---|---|
DISPATCH_SOURCE_TYPE_DATA_ADD | 自定义事件,变量增长 | n/a | n/a |
DISPATCH_SOURCE_TYPE_DATA_OR | 自定义事件,变量OR | n/a | n/a |
DISPATCH_SOURCE_TYPE_DATA_REPLACE | 自定义事件,变量REPLACE。若是传入的数据为0,将不会出发handler | n/a | n/a |
DISPATCH_SOURCE_TYPE_MACH_SEND | 监听Mach port的deadname通知,handle是具备send权限的Mach port 包括send或send_once | mach port | dispatch_source_mach_send_flags_t; // receive权限对应的send权限已被销毁,DISPATCH_MACH_SEND_DEAD 0x1 |
DISPATCH_SOURCE_TYPE_MACH_RECV | 监听Mach port获取待等待处理的消息 | mach port | dispatch_source_mach_recv_flags_t; n/a |
DISPATCH_SOURCE_TYPE_MEMORYPRESSURE | 监听系统中的内存压力 | n/a | dispatch_source_memorypressure_flags_t: DISPATCH_MEMORYPRESSURE_NORMAL 0x01 DISPATCH_MEMORYPRESSURE_WARN 0x02 DISPATCH_MEMORYPRESSURE_CRITACAL 0x04 |
DISPATCH_SOURCE_TYPE_PROC | 监听进程事件 | 进程ID | dispatch_source_proc_flags_t: |
DISPATCH_SOURCE_TYPE_READ | 监听文件描述符是否有可读的数据 | 文件描述符(int) | n/a |
DISPATCH_SOURCE_TYPE_SIGNAL | 监听当前进程的signal | signal number(int) | n/a |
DISPATCH_SOURCE_TYPE_TIMER | 定时器监听 | n/a | dispatch_source_timer_flags_t: // 系统将尽最大努力保持精度,可能会致使更高的系统能耗 DISPATH_TIMER_STRICT 0x1 |
DISPATCH_SOURCE_TYPE_VNODE | 监听文件描述符事件 | 文件描述符(int) | dispatch_source_vnode_flags_t: |
DISPATCH_SOURCE_TYPE_WRITE | 监听文件描述符使用可用的buffer空间来写数据 | 文件描述符(int) | n/a |
异步取消 dispatch_source,阻止events handler block被进一步调用;
/* @function dispatch_source_cancel * * @discussion * 取消“dispatch source”可阻止“events handler block”继续调用,但不会中断正在处理的“events handler block”。 * * 当“dispatch source”的“events handler”已经完成,那么“events handler”将会被提交到目标queue。 * 而且此时代表安全的关闭“dispatch source”的句柄(标志符,即文件描素符或mach port) * * See dispatch_source_set_cancle_handler() for moreinformation * * @param source * 要取消的“dispatch source” */ void dispatch_source_cancle(dispatch_source_t source);
测试dispatch_source是否被取消:
/* @function dispatch_source_testcancle * * @param source * 要测试的“dispatch source” * * @param result * 0:未被取消 1:被取消 */ long dispatch_source_testcancle(dispatch_source_t source);
合并数据的类型是 DISPATCH_SOURCE_TYPE_DATA_ADD,DISPATCH_SOURCE_TYPE_DATA_OR,DISPATCH_SOURCE_TYPE_DATA_REPLACE的dispatch sorce,而且提交相应的events handler block到指定的queue。
/* @function dispatch_source_merge_data * * @param source * 要合并数据的“diapatch source”。 * * @param value * 根据“diapatch source”的数据类型,指定value合并到挂起数据的操做:OR and ADD。 * 若是value为0,不会产生任何影响,也不会提交到相应的“events handler block”。 */ void dispatch_source_merge_data(dispatch_source_t source, unsigned long value);
配置“dispatch source timer”的开始时间(start time),interval(时间间隔),精度(leeway)。
/* @function dispatch_source_set_timer * * @discusstion * 一旦再次调用这个方法,那么以前的“source timer”数据会被清除。 * “source timer”下次触发的时间将会是“start”参数设置时间。 * 此后每次间隔“interval”纳秒将会继续触发,直到“source timer”被取消。 * * 系统可能会延迟调用“source timer”的触发,以提升功耗和系统性能。 * 对于刚开始“source timer”容许的最大延迟上限是“leeway”纳秒。 * 对于“start + N * interval”时间后触发的“time source”,上限为“MIN(leeway, interval/2)”, 下限由系统控制。 * * @param start 开始时间 * * @param interval “timer”时间间隔,单位是“纳秒”,使用“DISPATCH_TIMER_FOREVER”只发射一次的“timer” * * @param leeway “timer”精度,单位纳秒。 */ void dispatch_source_set_timer(dispatch_source_t source, dispatch_time_t start, uint64_t intercal, uint64_t leeway);
对指定的dispatch source设置event handler,用来响应dispatch source的触发。
/* @function dispatch_source_set_event_handler * * @param source * 要修改的“diapatch source”。 * * @param handler * 定义一个“cancle handler block”提交到“dispatch source”的指定目标queue。 */ void dispatch_source_set_event_handler(dispatch_source_t source, dispatch_block_t _Nullable handler); /* @function dispatch_source_set_event_handler_f * * @param source * 要修改的“diapatch source”。 * * @param handler * 定义一个“cancle handler block”提交到“dispatch source”的指定目标queue。 */ void dispatch_source_set_event_handler_f(dispatch_source_t source, dispatch_function_t _Nullable handler);
对指定的dispatch source设置cancle handler,用来响应dispatch source的取消。
/* @function dispatch_source_set_cancle_handler * * @discussion * 当调用“dispatch_source_cancle()”时,一旦系统释放了全部的“dispatch_source”下的“handle(句柄)”引用。 * 而且“events handler”已经被执行完毕,那么就会在指定目标queue上触发“cancle handler”。 * * 若是“dispatch_source”监听的是文件描述符和mach port,那么就须要使用“cancle handler”。 * 目的是安全的关闭文件描述符和销毁mach port。 * * 若是在触发“cancle handler”以前,文件描述符和mach port就已经被关闭和销毁,就有可能致使竞争条件。 * * Race condition竞争条件是指多个进程或线程并发访问和操做同一数据且执行结果与访问的特定顺序有关的对象。 * 即线程和进程之间访问数据的前后顺序决定了数据修改的结果。 * * 若是新文件的文件描述符被初始化和当前文件的文件描述符是同样的,当“dispatch source”的“events handler”仍在运行的时候。 * “events handler”有可能会在错误的文件描述符中read/write数据。 * * @param source * 要修改的“diapatch source”。 * * @param handler * 定义一个“cancle handler block”提交到“dispatch source”的指定目标queue。 */ void dispatch_source_set_cancle_handler(dispatch_source_t source, dispatch_block_t _Nullable handler); /* @function dispatch_source_set_cancle_handler_f * * @param source * 要修改的“dispatch source”。 * * @param handler * 定义一个“cancle handler block”提交到“dispatch source”的指定目标queue。 */ void dispatch_source_set_cancle_handler_f(dispatch_source_t source, dispatch_function_t _Nullable handler);
给指定的dispatch source设置一个registration handler。
/* @function dispatch_source_set_registration_handler * * @discussion * 若是指定了“registration handler”,会在相应的“kevents()”被注册到系统时, * 在“dispatch source”调用“dispatch_resume()”以前会被提交到指定的目标queue。 * 若是“dispatch source”已经被注册了,再来添加“registration handler”,这个“registration handler”会被当即执行。 * * @param source * 要被修改的“dispatch source”。 * * @param handler * 定义一个“registration handler”提交到dispatch source指定的queue。 */ void dispatch_source_set_registration_handler(dispatch_source_t source, dispatch_block_t _Nullable handler); /* @function dispatch_source_set_registration_handler_f * * @param source * 要被修改的“dispatch source”。 * * @param handler * 定义一个“registration handler”提交到dispatch source指定的queue。 */ void dispatch_source_set_registration_handler_f(dispatch_source_t source, dispatch_function_t _Nullable handler);
dispatch_source监听事件产生触发的handler。