高级IO技术详解:阻塞/非阻塞IO、多路复用与内存映射
关键词:
阻塞IO
非阻塞IO
select/poll/epoll
mmap
一、阻塞IO vs 非阻塞IO
类型 | 行为特点 | 设置方式 |
---|---|---|
阻塞IO | - 读空管道阻塞 - 写满管道阻塞 | 默认模式 |
非阻塞IO | - 读空文件返回 -1 ,errno=EAGAIN - 写满立即返回错误 | 1. open() 时加 O_NONBLOCK 标志2. 通过 fcntl() 设置:fcntl(fd, F_SETFL, O_NONBLOCK) |
二、IO多路复用(解决高并发IO问题)
1. 状态机模型
将IO任务抽象为状态流转,例如读操作的状态转换:
STATE_R → read()├── 返回值 >0 → STATE_W (准备写) ├── 返回值=0 → STATE_T (终止) ├── errno=EAGAIN → 保持STATE_R └── 其他错误 → STATE_E (异常)
2. select() 函数
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
- 核心操作:
FD_ZERO(&set); // 清空集合 FD_SET(fd, &set); // 添加fd FD_CLR(fd, &set); // 移除fd FD_ISSET(fd, &set);// 检查fd是否就绪
- 特点:
- 监听读/写/异常事件
- 文件描述符上限:
FD_SETSIZE
(通常1024) - 每次调用需重新初始化fd_set
- 超时控制可模拟
sleep
:select(0, NULL, NULL, NULL, &tv)
3. poll() 函数
struct pollfd {int fd; // 监听的文件描述符short events; // 监听的事件(POLLIN/POLLOUT)short revents; // 返回的事件
};
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
- 优势:
- 无文件描述符数量限制
- 监听与返回结果分离(通过
events
和revents
) - 无需每次重新初始化结构体
4. epoll(Linux专属高性能模型)
int epoll_create(int size); // 创建epoll实例
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); // 注册fd
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); // 等待事件
- 核心优势:
- 事件驱动,无需遍历所有fd
- 支持边缘触发(ET)与水平触发(LT)模式
- 百万级并发支持
三、内存映射IO(mmap)
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
- 功能:将磁盘文件直接映射到内存空间
- 应用场景:
- 零拷贝文件读写
- 大文件高效处理
- 进程间共享内存
四、关键总结
技术 | 适用场景 | 性能瓶颈 |
---|---|---|
select/poll | 低并发兼容性需求 | O(n) 轮询效率低 |
epoll | Linux高并发网络服务 | 无上限,事件驱动O(1) |
mmap | 大文件读写/进程通信 | 减少内核-用户态拷贝开销 |
扩展思考:
- 边缘触发(ET)模式下为何必须非阻塞IO?
mmap
写回磁盘的同步机制(msync()
)如何保证数据安全?- 异步IO(aio)与多路复用的本质区别?
原创声明:本文为博主原创笔记整理,转载请注明出处!