# 浅谈阻塞
网络IO的阻塞、同步大多都是类比,没有深入到实质。
接收数据包举例
- 数据准备阶段
数据包到达网卡后,通过DMA方式将数据包拷贝到内存中,然后经过硬中断和软中断,接着通过内核线程ksoftirqd经过内核协议栈的处理,将数据发送到内核Socket的接收缓冲区中。 - 数据拷贝阶段
数据包到达内核空间的Socket接收缓冲区后,需要拷贝到用户空间的内存中,才能够被应用程序读取。
# 阻塞与非阻塞的区别
阻塞与非阻塞的区别就发生在第一阶段,数据准备阶段。当应用程序发起启动系统调用(read)
,线程开始从用户态转换为内核态,
线程开始读取Socket接收缓冲区
中的网络数据。
阻塞与非阻塞的区别在于,当数据包还没有经过网卡到达RingBuffer缓冲区之前,也就是数据准备阶段,缓冲区没有数据包,阻塞:会一直等待;非阻塞:内核会返回错误标志EWOULDBLOCK。
相同点在于,都会等待第二阶段数据从内核到用户空间拷贝完成后,read调用才会返回。
# 同步与异步的区别
同步与异步的区别在第二阶段,数据拷贝阶段。数据拷贝阶段,主要是将内核空间的数据拷贝到用户空间,然后应用程序才可以读取到数据。
- 同步 同步模式是指,用户线程的内核态来执行第二阶段,所以应用程序会在第二阶段发生阻塞,因为此时用户线程在忙碌从内核空间往用户空间搬运数据,直至用户数据拷贝完成, 系统调用才会返回。linux的epoll和mac的kqueue都属于同步IO模型。
- 异步
异步IO模型是指内核来完成数据的拷贝操作,当内核执行完第二阶段,会
通知用户线程
IO操作已经完成,并且将数据回调
给用户线程。所以异步依赖内核的支持,也就是底层 操作系统的支持。目前流行的操作系统中,只有windows的IOCP属于真正的异步IO,实现的非常成熟。