用来做常用的多路复用的函数:
select 函数:
sizeof(fd_set) = 128
int select(int nfds, fd_set * readfds, fd_set * writefds,
fd_set* exceptfds, struct timeval * timeout);
-nfds: 委托内核检测的最大文件描述符的值+1;
-readfds: 检测文件描述符的读的集合,委托内核检测那些文件描述符的读的属性
-writefds: 检测文件描述符写的集合;
-exceptfds: 检测发生异常的文件描述符的集合
-timeout:设置的超时时间
struct timeval{
long tv_sec;
long tv_usec;
}
- null: 永久阻塞,直到检测到文件描述符有变化
- tv_sec = 0 tv_usec = 0,不阻塞
- tv_sec > 0 tv_usec > 0,阻塞对应的时间
- 返回值
- -1: 失败
- n: 检测集合中有n个文件描述符发生了变化
void FD_CLR(int fd, fd_set* set);
//将参数文件fd对应的set中的地方置为0;
int FD_ISSET(int fd, fd_set* set);
void set(int fd, fd_set* set);
void FD_ZERO(fd_set * set);
缺点:
- 每次调用select 都要把fd 集合从用户态拷贝到内核态;
- 每次调用select 都需要在内核遍历传递进来的所有fd,开销很大;
- select 支持的文件描述符数量太小了;
- fds集合不能复用,每次都需要重置;
poll:
struct pollfd{
int fd; // 委托检测的文件描述符
short events; // 委托内核检测什么事件
short revents; // 文件描述符实际发生的事件
};
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
- fds 是一个struct pollfd的结构体数组,需要检测的集合;
- nfds 这是第一个参数数组中最后一个有效元素的下标
- timeout
- 返回值:
- -1 失败
- > 0(n) 检测到集合中有n个发生变化;
epoll:
// 需要检测的文件描述符(红黑树),还有就绪链表,存放检测到的文件信息(双链表)
int epoll_create(int size);
- 返回值:
- -1 失败
- >0 文件描述符
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *even);
- epfd: epoll 实例对应的文件描述符
- op: 需要什么操作:
- EPOLL_CTL_ADD
- EPOLL_CTL_MOD
- EPOLL_CTL_DEL
- fd:
- event : 检测文件描述符的什么动作
struct epoll_event{
uint32_t events;
epoll_data_t data;
};
常见的epoll 检测事件:
-EPOLLIN
-EPOLLOUT
-EPOLLERR
typedef union epoll_data{
void *ptr;
int fd;
uint32_t u32;
uint32_t u64;
}epoll_data_t;
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
- epfd: 对应的文件描述符
- events: 传出参数,保存变化了的文件描述符的信息;
- maxevents: 第二参数的大小
- timeout: 阻塞时间
- 返回值:
- -1 是失败
- >0 是改变的个数
两种模式:
LT(水平触发):支持非阻塞和阻塞。内核会告诉文件描述符是否就绪,如果不做操作,还是会继续通知。
假设委托内核检测读事件 -> 检测fd 的读缓冲区
读缓冲区有数据 -> epoll 检测到了会给用户通知
a. 用户不读,数据一直在缓存区,epoll会一直通知
b. 用户读一部分,epoll会一直通知
c. 缓冲区数据读完,不通知
ET(边沿触发):只能支持非阻塞,只会发送一场通知。描述符从未就绪到就绪时,会通知,但是如果不对fd做IO 内核不会再次发送通知。
假设委托内核检测读事件 -> 检测fd 的读缓冲区
读缓冲区有数据 -> epoll 检测到了会给用户通知
a. 用户不读,数据一直在缓存区,epoll下次检测就不通知
b. 用户读一部分,epoll下次检测就不通知
c. 缓冲区数据读完,epoll下次检测就不通知
ET很大程度上减少了epoll 事件被触发的次数,效率比LT 要高。但是必须要使用非阻塞的套接口,以防止文件描述符饿死。