操作方式: 创建文件描述符
添加文件描述符
通知内核开始监测
根据返回的结果做对应操作
应用场景:1. 构建并发服务器,使用IO多路复用监测多个客户端套接字
2. 使用io多路复用监测多个IO所对应的通信(如:网络、串口、can....)
3. 在阻塞io中,进行超时监测
fd_set rdfds;
fd_set tmpfds;
//清空集合
FD_ZERO(&rdfds);
//添加关注的文件描述符到集合中
FD_SET(0, &rdfds);
maxfd = 0 > maxfd ? 0 : maxfd;
FD_SET(fifofd, &rdfds);
maxfd = fifofd > maxfd ? fifofd : maxfd;
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
功能:监听文件描述符集合
参数: nfds:监测的文件描述符上限值(最大文件描述符的值+1)
readfds:读文件描述符集合
writefds:写文件描述符集合
exceptfds:异常条件的描述符集合
timeout:设置超时时间,NULL:一直等待
返回值:成功返回产生事件文件描述符个数,失败返回-1 ,定时时间到达仍没有事件产生返回0
void FD_CLR(int fd, fd_set *set);将fd从文件描述符集合中清除
int FD_ISSET(int fd, fd_set *set);判断文件描述符fd是否仍在文件描述符集合中
void FD_SET(int fd, fd_set *set);将fd加入文件描述符集合中
void FD_ZERO(fd_set *set);文件描述符集合清0
tmpfds = rdfds;
//开始监测集合中的io
int cnt = select(maxfd+1, &tmpfds, NULL, NULL, NULL);
if (cnt < 0)
{
perror("fail select");
return -1;
}
if (FD_ISSET(0,&tmpfds))
{
memset(buff, 0, sizeof(buff));
fgets(buff, sizeof(buff), stdin);
printf("STDIN : %s\n", buff);
}
if (FD_ISSET(fifofd, &tmpfds))
{
memset(buff, 0, sizeof(buff));
read(fifofd, buff, sizeof(buff));
printf("FIFO : %s\n", buff);
}
缺点:1.select监听文件描述符最大个数为1024 (数组) O(n)
2.select监听的文件描述符集合在用户层,需要应用层和内核层互相传递数据
3.select需要循环遍历一次才能找到产生的事件
4.select只能工作在水平触发模式(低速模式)无法工作在边沿触发模式(高速模式)
缺点: 1.poll监测文件描述符不受上限限制 (链表) O(n)
2.poll监听的文件描述符集合在用户层,需要内核层向用户层传递数据
3.poll需要循环遍历一次才能找到产生的事件
4.poll只能工作在水平触发模式(低速模式)无法工作在边沿触发模式(高速模式)
int epoll_create(int size);
功能:创建一个监听事件表(内核中)
参数:size:监听事件最大个数
返回值:成功返回非负值:表示epoll事件表对象(句柄),失败返回-1
int epfds = epoll_create(2);
if (-1 == epfds)
{
perror("fail epoll_create");
return -1;
}
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
功能:在监听事件表中新增一个事件
参数: epfd:事件表文件描述符
op: EPOLL_CTL_ADD 新增事件
EPOLL_CTL_MOD 修改事件
EPOLL_CTL_DEL 删除事件
fd:文件描述符
events:事件相关结构体
返回值:成功返回0 ,失败返回-1
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
events: EPOLLIN 读事件
EPOLLOUT 写事件
EPOLLET 边沿触发
LT 水平触发
struct epoll_event ev;
ev.events = event;
ev.data.fd = fd;
int ret = epoll_ctl(epfds, EPOLL_CTL_ADD, fd, &ev);
if (-1 == ret)
{
perror("fail epoll_ctl add");
return -1;
}
int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
功能:监听事件表中的事件,并将产生的事件存放到结构体数组中
参数:epfd:事件表文件描述符
events:存放结果事件结构体数组空间首地址
maxevents:最多存放事件个数
timeout:超时时间,-1:阻塞等待直到有事件发生
返回值:成功返回产生事件个数,失败返回-1 ,超时时间到达没有事件发生返回0
int cnt = epoll_wait(epfds, evs, 2, -1);
if (cnt < 0)
{
perror("fail epoll_wait");
return -1;
}
for (int i = 0; i < cnt; i++)
{
if (0 == evs[i].data.fd)
{
memset(buff, 0, sizeof(buff));
fgets(buff, sizeof(buff), stdin);
printf("STDIN: %s\n", buff);
}
else if (fifofd == evs[i].data.fd)
{
memset(buff, 0, sizeof(buff));
ssize_t size = read(evs[i].data.fd, buff, sizeof(buff));
if (size <= 0)
{
epoll_del_fd(epfds, evs[i].data.fd);
close(evs[i].data.fd);
continue;
}
printf("FIFO: %s\n", buff);
}
}
5、epoll_ctl 从事件集合中删除完成的文件描述符
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int ret = epoll_ctl(epfds, EPOLL_CTL_DEL, fd, NULL);
if (-1 == ret)
{
perror("fail epoll_ctl del");
return -1;
}
优点: 1.epoll创建内核事件表,不受到文件描述符上限限制 (红黑树) O(logn)
2.epoll监听的事件表在内核中,直接在内核中监测事件效率高
3.epoll会直接获得产生事件的文件描述符的信息,而不需要遍历检测
4.epoll既能工作在水平触发模式,也能工作在边沿触发模式