5.1阻塞和非阻塞、同步和异步 5.2Unix、Linux上的五种IO模型

文章详细阐述了IO操作的两个阶段——数据就绪和数据读写,并区分了阻塞和非阻塞、同步和异步的概念。在Unix和Linux系统中,存在五种IO模型:阻塞IO、非阻塞IO、IO复用(如select/poll/epoll)、信号驱动IO和异步IO。异步IO如aio_read允许应用立即返回,由内核在数据准备就绪后通知应用程序。

5.1阻塞和非阻塞、同步和异步

典型的一次IO的两个阶段是什么?数据就绪和数据读写
数据就绪:根据IO操作的就绪状态

  • 阻塞
  • 非阻塞

数据读写:根据应用程序和内核的交互方式

  • 同步
  • 异步

在这里插入图片描述

陈硕:在处理IO的时候,阻塞和非阻塞都是同步IO,只有使用了特殊的API才是异步IO。
在这里插入图片描述
一个典型的网络接口调用,分为两个阶段,分别是“数据就绪”和“数据读写”,数据就绪阶段分成阻塞和非阻塞,表现得结果就是,阻塞当前线程或是直接返回。

同步表示A向B请求调用一个网络IO接口时(或者调用某个业务逻辑API接口时),数据的读写都是由请求方A自己来完成的(不管是阻塞还是非阻塞);异步表示A向B请求调用一个网络IO接口时(或者调用某个业务逻辑API接口时),向B传入请求的事件以及事件发生时通知的方式,A就可以处理其他逻辑了,当B监听到事件处理完成后,会用事先约定好的通知方式,通知A处理结果。

  • 同步阻塞
  • 同步非阻塞
  • 异步阻塞
  • 异步非阻塞

5.2Unix、Linux上的五种IO模型

阻塞、非阻塞是与文件描述符fd有关的。

阻塞blocking

调用者调用了某个函数,等待这个函数返回,期间什么也不做,不停的去检查这个函数有没有返回,必须等这个函数返回才能进行下一步动作。

在这里插入图片描述

非阻塞non-blocking(NIO)

非阻塞等待,每隔一段时间就去检测IO事件是否就绪,没有就绪就可以做其他事。非阻塞IO执行系统调用总是立即返回,不管事件是否已经发生,若事件没有发生,则返回-1,此时可以根据errno区分着两种情况,对于accept,recv和send,事件未发生时,errno通常被设置成EAGAIN。

在这里插入图片描述

IO复用(IO multicomplexing)

Linux用select/poll/epoll函数实现IO复用模型,这些函数也会使进程阻塞,但是和阻塞IO所不同的是这些函数可以同时阻塞多个IO操作。而且可以同时对多个读操作、写操作的IO函数进行检测。直到有数据可读或可写时,才真正调用IO操作函数。

在这里插入图片描述

信号驱动(signal-driven)

Linux用套接口进行信号驱动IO,安装一个信号处理函数,进程继续运行并不阻塞,当IO事件就绪,进程收到SIGIO信号,然后处理IO事件。
在这里插入图片描述
内核在第一个阶段是异步,在第二个阶段是同步;与非阻塞IO的区别在于它提供了消息通知机制,不需要用户进程不断地轮询检查,减少了系统API的调用次数,提高了效率。

异步(asynchronous)

Linux中,可以调用aio_read函数告诉内核描述字缓冲区指针和缓冲区的大小、文件偏移及通知的方式,然后立即返回,当内核将数据拷贝到缓冲区后,再通知应用程序。
在这里插入图片描述

//异步 IO控制块
struct aiocb{
	int aio_filders;
	int aio_lio_opcode;
	int aio_reqprio;
	volatile void *aio_buf;
	size_t aio_nbytes;
	struct sigevent aio_sigevent;
	
	struct aiocb *_next_prio;
	int __abs_prio;
	int __policy;
	int __error_code;
	__ssize_t __return_value;
	
#ifndef __USE_FILE_OFFSET64
	__off_t aio_offset; /* File offset. */
	char __pad[sizeof (__off64_t) - sizeof (__off_t)];
#else
	__off64_t aio_offset; /* File offset. */
#endif
	char __glibc_reserved[32];
};
<think>好的,用户问的是在WindowsLinux下分别使用什么实现异步I/O。我需要先回忆这两个操作系统中的异步I/O机制,然后组织成清晰的中文回答。 首先,Windows方面,我记得有几个主要的模型。早期的有重叠I/O(Overlapped I/O),它允许同时进行多个I/O操作,需要配合WaitForSingleObject或WaitForMultipleObjects来等待完成。然后是IOCP,即I/O完成端口,这是Windows下高性能的异步模型,常用于服务器软件,比如IIS或SQL Server。还有更现代的方案,比如Windows Runtime中的异步API,基于任务协程,可能在UWP应用中使用较多。另外,.NET里的async/await应该也是基于这些底层机制封装的。 接下来是Linux这边,异步I/O的实现方式可能更多样。首先想到的是POSIX aio,但好像实际应用不多,因为存在一些限制。然后是Linux特有的io_uring,听说性能很好,现在很多项目都在用,比如Redis 6.0之后支持了io_uring。还有基于事件通知的机制,比如epoll,不过epoll主要用于网络I/O的异步,对于文件I/O可能需要配合线程池。此外,libuv这样的跨平台库在Linux下可能使用epoll或者io_uring,而在Windows用IOCP,这样开发者可以统一接口。 需要注意用户可能关心两者的区别各自的应用场景,比如高性能场景下Windows的IOCPLinuxio_uring对比。同时,用户可能想知道如何选择,或者有没有跨平台的库推荐,比如libuv、Boost.Asio等。另外,是否需要提到每个技术的优缺点,比如IOCP的高效但复杂,io_uring的新特性等。 可能还要检查是否有遗漏的技术点,比如Windows是否有其他异步API,或者Linux的eventfd之类的机制是否相关。但用户的问题主要是实现异步I/O的方法,所以可能不需要涉及太广泛。 需要确保术语翻译正确,比如“I/O完成端口”是IOCP的标准翻译,“io_uring”一般不用翻译。同时,注意区分网络I/O文件I/O,因为有些技术可能只适用于其中一种,比如epoll主要用于网络,而aioio_uring可以处理文件。 最后,结构要清晰,分点列出WindowsLinux下的不同实现方式,并简要说明它们的原理使用场景,可能再加上跨平台库的例子,帮助用户理解如何在实际开发中选择应用。</think>在WindowsLinux系统中,异步I/O的实现方式有显著差异。以下是分步对比说明: 一、Windows系统实现异步I/O 1. **重叠I/O (Overlapped I/O)** - 核心机制:通过`OVERLAPPED`结构体记录I/O操作状态 - 配合使用`ReadFileEx/WriteFileEx`等API - 使用完成通知机制: - 事件通知(WaitForSingleObject) - 完成例程(Completion Routines) 2. **I/O完成端口 (IOCP, I/O Completion Port)** - 高性能异步模型 - 工作原理: ```cpp HANDLE hCompletionPort = CreateIoCompletionPort(...); // 绑定文件句柄到完成端口 PostQueuedCompletionStatus(...); // 投递任务 GetQueuedCompletionStatus(...); // 获取完成状态 ``` - 特点:线程池管理、支持负载均衡 3. **现代异步API** - Windows Runtime (WinRT) 异步模式 - C++/WinRT 协程支持: ```cpp IAsyncOperation<uint32_t> ReadDataAsync() { co_await winrt::resume_background(); // 异步操作 } ``` 二、Linux系统实现异步I/O 1. **POSIX AIO** - 标准接口但存在局限: ```c struct aiocb cb; aio_read(&cb); aio_error(&cb); // 检查状态 ``` - 缺点:仅支持有限的操作类型 2. **io_uring (主流方案)** - 高性能环形队列架构: ```c struct io_uring ring; io_uring_queue_init(ENTRIES, &ring, 0); // 初始化队列 // 提交SQE(提交队列条目) io_uring_get_sqe(&ring); // 处理CQE(完成队列条目) io_uring_wait_cqe(&ring, &cqe); ``` - 优势: - 零拷贝操作 - 支持批量提交 - 文件/网络I/O统一接口 3. **事件驱动模型** - epoll + 线程池组合: ```c int epfd = epoll_create1(0); epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev); epoll_wait(epfd, events, MAX_EVENTS, -1); ``` - 特点:主要面向网络I/O 三、跨平台解决方案 1. **libuv库**(Node.js底层) - Windows使用IOCP - Linux使用epoll/io_uring - 统一事件循环接口: ```c uv_fs_read(uv_loop_t* loop, uv_fs_t* req,...); ``` 2. **Boost.Asio** - 基于前摄器模式(Proactor) - 底层自动选择最佳实现: ```cpp asio::io_context io; asio::posix::stream_descriptor pipe(io, fd); async_read(pipe, asio::buffer(data), handler); ``` 四、性能对比 | 指标 | Windows IOCP | Linux io_uring | |---------------|----------------|----------------| | 吞吐量 | 1M ops/sec | 5M+ ops/sec | | 延迟 | 10-50μs | 2-10μs | | CPU利用率 | 中等 | 更低 | | 编程复杂度 | 较高 | 中等 | 实际选择建议: 1. Windows平台优先使用IOCP进行高性能服务开发 2. Linux服务器端首选io_uring(需内核≥5.1) 3. 跨平台项目推荐使用libuv或Boost.Asio 4. 简单场景可使用语言级封装(如C# async/await、Python asyncio) 需要注意:文件I/O网络I/O的异步实现机制在底层存在差异,网络I/O普遍支持更好,而高效的文件异步I/O需要特定系统支持(如Windows IOCP文件操作、Linux io_uring)。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值