高級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)與多路復用的本質區別?
原創聲明:本文為博主原創筆記整理,轉載請注明出處!