IO模型 + select、epoll 核心知识点
标签:Linux网络编程 IO多路复用 select epoll TCP并发
目录
- 一、4种常见IO模型
- [二、select 知识点](#二、select 知识点)
- [1. fd_set 集合操作宏](#1. fd_set 集合操作宏)
- [2. select函数](#2. select函数)
- [3. select缺点(考试重点)](#3. select缺点(考试重点))
- [三、epoll 三大API](#三、epoll 三大API)
- [1. epoll_create](#1. epoll_create)
- [2. epoll_ctl](#2. epoll_ctl)
- [3. epoll_wait](#3. epoll_wait)
- [四、epoll while(1)循环核心逻辑(高频代码考点)](#四、epoll while(1)循环核心逻辑(高频代码考点))
- [五、select VS epoll对比](#五、select VS epoll对比)
- 六、事件标记
一、4种常见IO模型
-
阻塞IO
没有数据时CPU将任务挂起,任务不占用CPU资源,效率高。
-
非阻塞IO
没有数据时,程序不停轮询检测数据是否到来,CPU持续执行代码,效率低。
-
信号驱动IO(异步IO)
把fd设置为异步IO,内核监测fd事件;事件发生内核发送SIGIO信号通知用户层,程序捕获信号处理IO。
-
多路复用IO
由内核同时监听一堆文件描述符,只要其中任意fd就绪,就返回给应用层,主要实现:
select、epoll。
补充:多线程TCP并发:
每个客户端分配一个独立线程;线程拥有独立栈空间,并发数量有限,客户端太多有系统崩溃风险;实际开发一般搭配线程池使用。
二、select 知识点
1. fd_set 集合操作宏
| 宏 | 作用 |
|---|---|
FD_ZERO(fd_set *set) |
清空文件描述符集合 |
FD_SET(int fd, fd_set *set) |
将fd加入集合 |
FD_CLR(int fd, fd_set *set) |
把fd从集合清除 |
FD_ISSET(int fd, fd_set *set) |
判断fd是否在就绪集合中 |
2. select函数
c
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
- 参数
nfds:最大文件描述符 + 1readfds:读事件fd集合;writefds:写事件fd集合;exceptfds:异常事件集合timeout:超时时间。传NULL代表永久阻塞等待事件。
- 返回值
*0:就绪的fd个数
- -1:函数出错
- 0:超时,没有任何事件发生
3.select缺点(考试重点)
- 返回后集合会被内核修改,每次循环必须重新FD_ZERO、FD_SET;
- 需要循环从
0~nfds‑1遍历全部fd,调用FD_ISSET判断是否就绪; - 文件描述符数量有上限。
三、epoll 三大API
1. epoll_create
c
int epoll_create(int size);
- 功能:创建内核事件监听表
- 参数:size现在被内核忽略,随便传正数
- 返回:成功返回epoll的文件描述符epfd;失败返回‑1
2. epoll_ctl
c
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
- 功能:增、删、改内核事件表里的fd
- op操作选项
EPOLL_CTL_ADD:添加fd进事件表EPOLL_CTL_MOD:修改已经注册fd的监听事件EPOLL_CTL_DEL:从事件表删除fd
struct epoll_event
c
struct epoll_event{
uint32_t events; //监听的事件:EPOLLIN读 / EPOLLOUT写 / EPOLLET边沿触发
epoll_data_t data; //联合体,保存fd(常用data.fd)或者自定义指针
};
- 返回值:成功0,失败‑1
3. epoll_wait
c
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
- 功能:阻塞等待事件发生
- 参数
epfd:epoll实例fdevents:数组首地址,内核把已经就绪的事件拷贝到这个数组maxevents:events数组最大容量timeout:超时;‑1代表永久阻塞
- 返回值
*0:就绪fd的数量nready,只用遍历
0 ~ nready‑1- -1:出错
- 0:超时无事件
四、epoll while(1)循环核心逻辑(高频代码考点)
- 调用
epoll_wait()阻塞等待事件; - 根据返回的
nready,只循环nready次处理就绪事件; - 判断就绪fd:
- 如果是监听
sockfd:代表新客户端连接,accept()得到confd,用EPOLL_CTL_ADD加入epoll监听; - 如果是普通客户端
confd:客户端发来数据,执行读写处理函数;
- 如果是监听
- 客户端断开/出错:必须两步:
EPOLL_CTL_DEL移除fd +close(confd),防止资源泄漏、CPU死循环; - 处理完成回到循环开头继续
epoll_wait等待。
五、select VS epoll对比
- select:返回要遍历全部fd;内核修改传入的集合,每次循环要重置集合;fd有上限。
- epoll:内核直接把就绪事件给到events数组,只遍历就绪的nready个;不需要每次重置集合;支持海量文件描述符。
六、事件标记
EPOLLIN:可读事件(客户端发来数据 / 新连接到来)EPOLLOUT:可写事件EPOLLET:边沿触发模式;默认是水平触发