文章目录
  1. 1. 20170428周记

这篇是我今年4月28号的工作周记, 我偷懒就直接拿过来了.

20170428周记

在极困的状态下学习了同步/异步, 阻塞/非阻塞的概念, 以及select/poll/epoll的概念. 复习了一下Binder机制中数据传输的原理.

同步/异步, 阻塞/非阻塞容易混淆的原因就在于两者都涉及一个等待/非等待的状态.
先来讲何为同步/异步.
这对术语描述的是通信的方式, 即通信的一方在发送下一次消息前, 是否等待另一方返回消息, 等待则为同步, 不等待则为异步.
举个例子, 你打电话给携程问后天是否有到某地的特价机票, 携程客服说”你稍等我查一下”, 这期间你们俩都没挂电话, 等他查好了(可能几分钟也可能几天)告诉你结果. 这是同步.
客服说”我查一下, 查好了回你电话”然后他就挂电话了, 等查好了他就主动打给你. 这是异步.
注意, 所谓”等待”并不一定是什么都不做的”空等”.

阻塞/非阻塞关注的是在等待结果的时候程序的状态. 如果结果返回前, 程序被挂起, 则是阻塞, 否则非阻塞.
还是上面的例子, 如果你是阻塞式打电话, 无论客服是不是异步给你结果, 你都会在电话旁一直等着, 直到客服给你结果, 这期间你什么都不能做. 如果你是非阻塞式的, 那你打完电话(发起调用)之后, 如果客服没有立即给你结果, 你可以自己爱做啥做啥, 只需要时不时回来检查一下客服是不是给你结果了或者他是不是回电了.
所以其实阻塞/非阻塞, 跟异步/同步是两个完全无关的概念. 但是由于异步这个概念指的就是程序不等待返回结果, 所以异步一定是非阻塞的(当然, 你也可以把它”实现为阻塞”, 只不过这样做就失去了选择异步的意义, 从表现上跟”同步阻塞”无异).

好了, 下面来说select/poll/epoll.
这三个操作是linux系统的系统命令, 他们都属于I/O多路复用的实现. 他们都是同步I/O.
同步I/O操作, 指的是该操作会导致请求进程阻塞, 直到I/O操作完成. 异步I/O操作不导致请求进程阻塞. 我觉得这两个概念也是造成混淆的原因之一, 其实只要明白这里所说的阻塞/非阻塞, 跟编程语言层次(Java NIO)所说的阻塞/非阻塞, 所描述的不是同一个现象. 在NIO中, 也存在阻塞的过程(Selector.select()).
I/O多路复用的意思是, 用多个文件描述符同时服务多个I/O过程, 有任一一个处于就绪状态时就触发对应I/O过程继续执行, 整个过程用同一个进程来管理, 而不是每个I/O都自己新建一个进程.
区分同步/异步的方式很简单, 由于异步是注册一个回调, 让系统自动调用回调函数处理, 所以请求进程在发起请求后几乎不需要参与后续工作. 所以相对来说, 如果发起请求后, 请求进程需要通过某种方式(死循环轮询, 停下来等待, 抑或在子线程中做类似操作)去获知是否有返回结果并主动处理返回结果, 那就不是异步, 而是同步.

select, 就是在轮询所有注册的文件描述符, 直到有描述符就绪或操作超时, 才会返回. 由于它需要轮询所有文件描述符, 所以性能随文件描述符增多呈线性负相关.
poll定义了一个不同的数据结构用来表示I/O时的文件描述符, 等待的事件和实际返回的事件. 其余过程本质上和select相同, 也是轮询所有文件描述符来获得就绪状态的描述符.
epoll是在2.6的linux内核中加入的, 它不再是轮询所有文件描述符, 而是以回调的方式, 在文件描述符处于就绪状态后, epoll_ctl回调事先注册好的处理函数, 将就绪的文件描述符加入到就绪队列中, 并发出就绪通知, 而epoll_wait调用时会在参数中指明需要等待哪个文件描述符上的什么事件, 它去检查就绪队列并返回需要处理的事件数量, 所以整个过程的效率只与活跃的文件描述符数量有关, 在高并发的情况下优于select和poll. 同时, 它使用mmap让用户空间可以直接访问到内核空间写入的数据区域, 因此少了一次内核空间到用户空间的拷贝.

binder的底层调用的是epoll.

文章目录
  1. 1. 20170428周记