阻塞IO和非阻塞IO的区别

定义

阻塞是进程的一种状态,表示等待某个事件发生(如读写操作需要等待数据),进程阻塞就表示暂停执行了,和挂起类似。

  • 阻塞IO:称阻塞的文件描述符为阻塞IO
  • 非阻塞IO:称非阻塞的文件描述符为非阻塞IO

see: Linux高性能服务器编程 游双 P126

区别

阻塞IO

针对阻塞IO的系统调用(如read, write)可能因为无法完成而被系统挂起,直到等待的事件发生为止。举例:read和write,通常IO操作都是阻塞I/O的,也就是说当你调用read时,如果没有数据收到,那么线程或者进程就会被挂起,直到收到数据。
坏处:see: I/O多路复用技术(multiplexing)是什么? - 用心阁的回答 - 知乎
https://www.zhihu.com/question/28594409/answer/74003996

由于服务器可能要处理很多的连接,对于大部分的连接来说都是阻塞的,采用阻塞IO的模式可能会导致线程或者进程之间频繁切换,造成很大的系统开销。举个例子:当服务器需要处理1000个连接的的时候,而且只有很少连接忙碌的,那么会需要1000个线程或进程来处理1000个连接,而1000个线程大部分是被阻塞起来的。由于CPU的核数或超线程数一般都不大,比如4,8,16,32,64,128,比如4个核要跑1000个线程,那么每个线程的时间槽非常短,而线程切换非常频繁。这样是有问题的:

  • 线程是有内存开销的,1个线程可能需要512K(或2M)存放栈,那么1000个线程就要512M(或2G)内存。
  • 线程的切换,或者说上下文切换是有CPU开销的,当大量时间花在上下文切换的时候,分配给真正的操作的CPU就要少很多。

PS :实际上可能不需要这么多的线程,可以采用线程池的模式。
在这里插入图片描述
如上图所示,read直到数据复制到应用进程的缓冲区或者发生错误才会返回,这就是阻塞的定义:等待某个事件。

非阻塞IO

针对非阻塞IO的系统调用总是立刻返回,不管事件是否发生,如果事件没有立刻发生就返回-1,并且设置errno,就类似于出错了。对于accept,recv 和 send,事件未发生时,errno 通常被设置成 EAGAIN。
在这里插入图片描述

好处:如果等待的事件没有发生,如上图中所示的read数据未准备好,系统调用会立即返回而不是导致调用进程被挂起。
坏处:用户进程需要采用轮询的方式(逐个查找)查看事件状态,这往往耗费大量的CPU资源。

参考资料

Linux高性能服务器编程 游双
I/O多路复用技术(multiplexing)是什么? - 用心阁的回答 - 知乎
https://www.zhihu.com/question/28594409/answer/74003996

<think>好的,我现在需要回答用户关于Unix环境下阻塞IO非阻塞IO区别及用法的问题。首先,我要回顾一下用户提供的参考资料,确保我的回答准确。 根据引用[1],同步IO异步IO区别在于数据从内核拷贝到用户空间时是否阻塞进程,而阻塞IO非阻塞IO区别在于发起IO请求时是否阻塞。引用[4]提到在网络编程中,阻塞IO非阻塞IO的概念涉及socket的处理方式。 首先,我需要明确两者的基本区别阻塞IO在数据未就绪时会一直等待,线程被挂起,直到操作完成。而非阻塞IO会立即返回一个错误码,如EAGAIN或EWOULDBLOCK,线程可以继续执行其他任务,需要轮询检查状态。 接下来是实现的区别。用户可能需要知道如何设置非阻塞模式,比如使用fcntl函数设置O_NONBLOCK标志,或者使用select、poll、epoll等IO多路复用技术来管理多个非阻塞socket。这部分需要结合Unix系统调用来解释。 然后是关于使用场景。阻塞IO适合简单的应用程序,逻辑简单但并发能力差。非阻塞IO适合高并发场景,如网络服务器,但代码复杂度高。这里可以举例说明,比如Web服务器如何处理多个连接。 还需要注意两者的性能差异。非阻塞IO通过避免线程阻塞,提高了资源利用率,但需要更多的系统调用复杂的错误处理。而阻塞IO虽然效率低,但编程模型简单。 最后,用户可能需要知道如何在实际中实现这两种模式。比如,在socket编程中,创建socket时默认是阻塞的,如何更改为非阻塞,并配合多路复用技术使用。同时,要提到常见的函数如fcntl、select等。 检查是否有遗漏点,比如是否需要提到异步IO(但用户主要问阻塞非阻塞)。不过引用[1]提到了同步异步的区别,可以适当关联,但重点在阻塞非阻塞。 总结结构:先区别定义,再实现方式,然后使用场景,最后注意事项。确保引用正确,比如引用[1][4]中的内容,自然地插入引用标识。同时避免使用Markdown格式,保持自然的中文口语化。</think>### Unix 环境下阻塞 IO非阻塞 IO区别及实现方式 #### 一、核心区别 1. **阻塞 IO** - **定义**:当进程发起 IO 操作时,若数据未就绪(例如网络数据未到达或磁盘未响应),进程会被挂起(阻塞),直到数据就绪并完成拷贝到用户空间后才会继续执行[^1][^4]。 - **特点**:编程模型简单,但会占用线程资源,导致并发能力受限。 2. **非阻塞 IO** - **定义**:进程发起 IO 操作时,若数据未就绪,内核立即返回错误码(如 `EAGAIN` 或 `EWOULDBLOCK`),进程可继续执行其他任务,需通过轮询或事件通知机制主动检查数据状态[^1][^4]。 - **特点**:提高了资源利用率,但需要更复杂的逻辑处理(如重试、多路复用)。 #### 二、实现方式 1. **阻塞 IO 的实现** - **默认模式**:Unix 中大多数文件描述符(如 socket)默认是阻塞模式。 - **示例代码**(TCP 读取): ```c char buffer[1024]; ssize_t n = read(socket_fd, buffer, sizeof(buffer)); // 阻塞直到数据到达或出错 ``` 2. **非阻塞 IO 的实现** - **设置非阻塞标志**:通过 `fcntl` 函数为文件描述符添加 `O_NONBLOCK` 标志。 ```c int flags = fcntl(socket_fd, F_GETFL, 0); fcntl(socket_fd, F_SETFL, flags | O_NONBLOCK); ``` - **配合多路复用技术**:使用 `select`、`poll` 或 `epoll` 监听多个非阻塞描述符的就绪状态。 ```c fd_set read_fds; FD_ZERO(&read_fds); FD_SET(socket_fd, &read_fds); select(socket_fd + 1, &read_fds, NULL, NULL, NULL); // 等待数据就绪 ``` #### 三、使用场景 - **阻塞 IO**:适合简单、低并发的场景(如命令行工具)。 - **非阻塞 IO**:适合高并发服务(如 Web 服务器、实时通信系统),通过多路复用技术(如 `epoll`)管理海量连接。 #### 四、注意事项 - **性能权衡**:非阻塞 IO 减少了线程阻塞时间,但增加了系统调用次数(如轮询)代码复杂度。 - **错误处理**:非阻塞 IO 需处理 `EAGAIN` 等错误,并设计重试逻辑。 - **与异步 IO区别**:非阻塞 IO 仍是同步操作(需主动检查状态),而异步 IO(如 `aio_read`)由内核通知完成,无需轮询[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值