一. 同步异步,阻塞非阻塞与数据二次拷贝
1. 同步异步: 任务的执行顺序上区分
- 同步: 指一个任务, 只有当另一个任务返回后才能继续执行本任务
- 异步: 指一个任务只是发起一个通知告诉另一个任务可以执行了, 然后就继续执行自身的任务
同步可以保证2个任务的执行顺序, 而异步不能
2. 阻塞和非阻塞: 线程等待状态上区分
- 阻塞: 一个线程如果在等待另一个线程返回时, 自身处于挂起状态, 就是阻塞的
- 非阻塞: 反之则是非阻塞
3. 同步阻塞和同步非阻塞
同步表示必须保证任务顺序, 阻塞表示线程等待另一个线程时是否会被挂起, 因此:
- 同步非阻塞: 一个线程必须等待一个方法调用完毕才能继续执行接下来的任务, 且在等待过程中还可以执行其他方法, 则是同步非阻塞的
- 同步阻塞: 一个线程必须等待一个方法调用完毕才能继续执行接下来的任务, 但是等待过程中自己处于挂起状态, 则是同步阻塞的
4. 数据由 socket 设备到用户态经过两次拷贝
用户进程在读/写外存(socket 设备)上的数据时, 数据先从 socket 拷贝到内核空间, 然后再从内核空间拷贝到用户空间.
二. Linux IO模型
IO 模型是针对, 数据从 socket 到内核空间, 再到用户空间这两次拷贝过程中, 用户线程的状态
同步IO模型
如下各种同步阻塞 IO 模型, 第二步 内核空间->内核空间 的数据拷贝都是阻塞的, 通过阻塞的方式, 实现数据拷贝和用户进程使用的同步
1. 同步阻塞IO
同步阻塞IO, 数据拷贝的两个阶段 (socket -> 内核, 内核 -> 用户)中, 用户进程都是处于阻塞状态.

2. 同步非阻塞IO
当socket被设置成非阻塞的后
- socket -> 内核空间 : 用户进程不会阻塞 (轮训操作系统)
如果内核空间数据未拷贝完, 操作系统返回给用户进程一个错误码, 表示数据还未准备好. 此后用户进程要轮训询问操作系统数据拷贝进度. - 内核空间 -> 用户空间 : 用户进程阻塞
数据到内核空间复制完毕后, 用户进程进入阻塞, 等待数据从内核空间复制到用户空间
所以,nonblocking IO的特点是用户进程需要不断的轮训询问kernel数据好了没有。

3. IO多路复用(NIO)
IO多路复用将同步非阻塞 IO 的到内核态拷贝中, 用户进行轮训操作系统改为操作系统自动轮训查看 socket 数据是否拷贝完毕, 且可以同时轮训多个 socket. (epool)
前置知识: 每个 socket 连接, 在 Linux 系统中会被表示成一个文件描述符(fd)
IO多路复用过程如下:
- socket -> 内接空间 (用户进程阻塞, 但可以提前返回, 且不必自己轮训拷贝状态)
- 用户进程调用
select系统调用后进入阻塞状态 - 操作系统开始轮训所有 socket fd, 当]一个 socket fd 的数据可读/可写后(不用等到数据完全拷贝到内核空间), 在该 socket fd 上的 select 调用就会返回
- 用户进程调用
- 内核空间 -> 用户空间 (用户进程阻塞)
用户进程在 select 调用返回后变成就绪状态. 接着进行 recvFrom 调用, 将数据从内核空间拷贝到用户空间
IO多路复用主要解决服务器同时存在大量连接的情况, 是单线程处理多连接的模型;

上面提到的 select 和 epoll 主要有以下几个区别:
- select 的文件句柄数受限制, 最多能监听1024个 fd, 而 epoll 的 fd 个数没有限制
- select 采用轮训, fd 形成一个类似
数组的结构. epoll 采用队列的数据结构, 对于是否有 fd 可读或可写, 直接查看对应的队列是否为空即可. 这是因为epoll只会对"活跃"的socket进行操作, 每个fd上配置一个回调函数, 只有fd可读或可写时才会出发回调函数(将自己加入相应的队列中), 其他idle状态句柄则不会,在这点上,epoll实现了一个"伪"AIO - epool使用 mmap 加速内核与用户空间的消息传递。
无论是 select,poll 还是 epoll 都需要内核把 FD 消息通知给用户空间,如何避免不必要的内存拷贝就很重要,在这点上,epoll是通过内核于用户空间mmap同一块内存实现的。
4. 信号驱动IO
- socket -> 内核空间 (用户进程非阻塞)
信号驱动式 I/O 第一步采用在 socket 上注册回调函数(信号处理函数)的方式, 让操作系统自行拷贝. 用户进程在此期间并不阻塞。当数据准备好时,进程会收到一个 SIGIO 信号,可以在信号处理函数中调用 IO 操作函数处理数据。 - 内核空间 -> 用户空间 (用户进程阻塞)

异步IO模型
5. 异步非阻塞IO(AIO)
第一步和第二步, 用户进程都是非阻塞的
异步非阻塞 IO 是说, 用户发起 aio_read 操作之后, 函数立刻返回, 用户进程开始做其它工作, socket 数据复制到内核空间, 和内核空间复制到用户空间这两个步骤都是操作系统完成的. 两步完成后, 操作系统会给用户进程发送一个 signal 或执行一个基于线程的回调函数来完成这次 IO 处理过程,告诉它 read 操作完成了.

总结: 几种IO之间的区别

本文详细探讨了同步与异步任务执行的区别,阻塞与非阻塞状态在IO中的应用,以及数据在socket间的两次拷贝。重点介绍了同步阻塞IO、同步非阻塞IO、IO多路复用(NIO)和信号驱动IO模型,并对比了它们的特点。
711

被折叠的 条评论
为什么被折叠?



