select 是 Python 標準庫提供的最基礎 I/O 多路複用接口,直接封裝系統調用 select(2)。雖然性能不如 epoll/kqueue/IOCP,但跨平台(Windows/Linux/macOS)兼容性最好,且 API 簡潔,是理解異步網絡編程模型的最佳切入點。本文從參數語義、返回值處理、邊緣觸發/水平觸發區別講起,構建一個雙文件並發複製服務器演示完整流程。
(rlist, wlist, xlist)
fileno())。None 表示永久阻塞,0 表示非阻塞輪詢。select 就會反覆報告就緒,直到讀空。(src, dst) 映射 fd_map = {s.fileno(): (s, d) for s, d in zip(sources, destinations)} while True: # 僅監聽可讀事件 rlist, _, _ = select.select(list(fd_map.keys()), [], []) if not rlist: continue for fd in rlist: src, dst = fd_map[fd] n = read_write(src, dst) total += n if n == 0: # EOF del fd_map[fd] if not fd_map: # 全部完成 return total # 使用示例 if __name__ == "__main__": src1 = open("/etc/fstab", "rb") src2 = open("/etc/passwd", "rb") dst1 = open("/tmp/fstab.copy", "wb") dst2 = open("/tmp/passwd.copy", "wb") try: copied = copy_multiple([src1, src2], [dst1, dst2]) print(f"總共複製 {copied} 字節") finally: for f in (src1, src2, dst1, dst2): f.close()
fileno():普通 open() 返回的文件對象滿足;io.BytesIO、socket.socket 也支持。select 能在多文件間交替,實現「並發」效果。read() 返回空字節串表示 EOF,應從監聽集合移除對應 fd。xlist 捕獲帶外數據或連接重置。| 特性 | select | poll | epoll (Linux) |
|---|---|---|---|
| 最大 fd 數 | 1024 (FD_SETSIZE) | 無硬限制 | 無硬限制 |
| 時間複雜度 | O(n) 全掃描 | O(n) 全掃描 | O(1) 就緒回調 |
| 觸發模式 | 水平觸發 | 水平觸發 | 水平/邊緣觸發 |
| 跨平台 | 全平台 | Unix-like | Linux 專用 |
| Python 接口 | select.select | select.poll | select.epoll |
asyncio(內部自動選擇 epoll/kqueue/IOCP)。select 是零依賴備選。aiofiles + asyncio 或線程池 run_in_executor,select 對普通文件幫助有限(磁盤通常不阻塞)。select 僅支持 Socket,不支持管道/文件,跨平台腳本需注意。select 是 I/O 多路複用的「通用語言」,理解其「三個列表、阻塞等待、返回就緒集合」模型,是掌握 poll、epoll、asyncio 事件循環的基石。雖然現代高性能服務很少直接用 select,但它在教學、輕量腳本、跨平台兼容場景仍不可替代。