slect( )poll( )epoll( )函数详解
Posted 正在起飞的蜗牛
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了slect( )poll( )epoll( )函数详解相关的知识,希望对你有一定的参考价值。
1、slect()函数
1.1、函数原型
#include <sys/time.h>
int select(int maxfdp, fd_set* readfds, fd_set* writefds, fd_set* errorfds, struct timeval* timeout);
(1)maxfdp:指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错;
(2)readfds:要监视读操作的文件描述符集合;
(3)writefds:要监视写操作的文件描述符集合;
(4)errorfds:要监视出错操作的文件描述符集合;
(5)timeout:函数返回超时;
(6)返回值:负值–出错,0–超时返回,正值–有文件可读,或可写,或可读可写;
补充:fd_set有专门的宏来操作,不必深究细节;
1.2、timeout值说明
struct timeval
long tv_sec; /*秒 */
long tv_usec; /*微秒 */
1.3、struct fd_set集合的操作宏
FD_ZERO(fd_set*); //清空集合,相当于memset
FD_SET(int, fd_set*); //将一个给定的文件描述符加入集合之中
FD_CLR(int, fd_set*); //将一个给定的文件描述符从集合中删除
FD_ISSET(int, fd_set*); //检查集合中指定的文件描述符是否可以读写
1.4、示例代码
int status = -1;
fd_set fds_r;
fd_set fds_w;
//将集合清零
FD_ZERO(&fds_r);
FD_ZERO(&fds_w);
//文件描述符fd1添加到读监视集合
FD_SET(fd1,&fds_r);
//文件描述符fd1添加到写监视集合
FD_SET(fd2,&fds_w);
//文件描述符的最大值加1
maxfdp=fd1>fd2 ? fd1+1 : fd2+1;
//监视集合中的文件描述符,直到有文件可读会可写才返回
status = select(maxfdp,&fds_r, &fds_w, NULL, NULL)
if(status <= 0)
//出错,什么也不做
else //说明有文件描述符可读或者可写,需要轮询式的查询所有监视的文件描述符集合
if (FD_ISSET(fd1,&fds_r))
read(fd1, buf, size);
if (FD_ISSET(fd2,&fds_w))
write(fd2, buf, size);
2、poll()函数
2.1、函数原型
#include <poll.h>
int poll(struct pollfd fd[], nfds_t nfds, int timeout);
(1)fd:是一个struct pollfd数组,保存要监控的文件描述符信息;
(2)nfds:要监视的文件描述符的数据;
(3)timeout:超时时间,单位是毫秒;
(4)返回值:要么超时返回,要么阻塞住;返回值n>0时,表示有n个文件描述符发生了相应的事件;
2.2、timeout值说明
(1)INFTIM:永远等待,阻塞住;
(2)0:立即返回,不阻塞进程;
(3)>0:阻塞指定时间后返回;
2.3、struct pollfd结构体
struct pollfd
int fd; //要监视的文件描述符
short events; //要监视fd将发生的事件
short revents; //poll函数返回时,fd发生的的事件
;
2.4、事件标志
POLLIN 普通或优先级带数据可读
POLLRDNORM 普通数据可读
POLLRDBAND 优先级带数据可读
POLLPRI 高优先级数据可读
POLLOUT 普通数据可写
POLLWRNORM 普通数据可写
POLLWRBAND 优先级带数据可写
POLLERR 发生错误
POLLHUP 发生挂起
POLLNVAL 描述字不是一个打开的文件
常用的是POLLIN(可读)和POLLOUT(可写);
2.5、示例代码
//要监控的文件描述符数量
int fdCnt = 2;
//申请保存文件描述符的struct pollfd结构体
struct pollfd *pollfd;
pollfd = (struct pollfd*)calloc(fdCnt, sizeof(struct pollfd));
//fd1文件描述符监视是否可读
pollfd[0].fd = fd1;
pollfd[0].events = POLLIN;
//fd2文件描述符监视是否可写
pollfd[1].fd = fd2;
pollfd[1].events = POLLOUT;
//监视文件描述符并且阻塞,知道有文件描述符发生相应的事件
numready = poll(pollfd, fdCnt, INFTIM);
//轮询监视的文件描述符
for(i=0; i< numready && fdCnt > 0; i++)
if(pollfd[i].revents & POLLIN)
read(pollfd[i].fd, buf, size)
i++;
if(pollfd[i].revents & POLLIN)
write(pollfd[i].fd, buf, size)
i++;
fdCnt--;
3、epoll()函数
3.1、epoll_create()函数
//创建一个epoll对象,同时返回该对象的描述符
int epoll_create(int size);
(1)size:要监听的文件描述符的数量;
(2)返回值:创建的epoll对象描述符;
3.2、epoll_ctl()函数
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
(1)epfd:epoll_create()函数返回的epoll对象描述符;
(2)op:本次的操作;
(3)fd:要监听的文件描述符;
(4)event:关联要监听的fd,里面会对要监听的fd进行设置;
3.3、参数op
值 | 含义 |
---|---|
EPOLL_CTL_ADD | 将参数 fd 指定的描述符添加到 epoll 对象中,同时将其关联到一个 epoll 事件对象——即参数 event 所指定的值 |
EPOLL_CTL_MOD | 修改描述符 fd 所关联的事件对象 event,前提是该 fd 已经添加到了 epoll 对象中 |
EPOLL_CTL_DEL | 将描述符 fd 从 epoll 对象中移除,此时参数 event 被忽略,也可指定为 NULL |
3.4、fd监听的事件
值 | 含义 |
---|---|
EPOLLIN | 监听 fd 是否可读 |
EPOLLOUT | 监听 fd 是否可写 |
EPOLLRDHUP | Linux 2.6.17 后可用。监听流式套接字对象是否关闭或半关闭 |
EPOLLPRI | 监听是否有紧急数据可读 |
3.5、struct epoll_event结构体
typedef union epoll_data
void *ptr;
int fd; //监听的文件描述符fd
uint32_t u32;
uint64_t u64;
epoll_data_t;
struct epoll_event
uint32_t events; /* fd要监听的事件 */
epoll_data_t data; /* 用户数据,和fd绑定 */
;
(1)events:fd要监听的事件;
(2)data:fd绑定的数据,将来epoll_wait()函数返回发生事件的fd时,会返回fd绑定的epoll_data_t结构体数据;
3.6、epoll_wait()函数
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
(1)epfd:epoll_create()函数返回的epoll对象描述符;
(2)events:外部定义的一个struct epoll_event结构体数据,将地址传进去,epoll_wait()函数在返回时会将发生
事件的fd信息填充到该结构体中;
(3)maxevents:告诉epoll_wait()函数,events数组有几个成员;
(4)timeout:超时时间;
3.7、示例代码
//创建epoll对象,最多监听4个文件描述符
int epfd = epoll_create(4);
struct epoll_event ev;
ev.data.fd = fd1; // 注意这个值必须要指定,不然 epoll_wait 返回了你也不知道是谁发生了事件
ev.events = EPOLLIN; // 想监听可读事件,因为没有指定 EPOLLET 选项,所以默认是水平触发
epoll_ctl(epfd, EPOLL_CTL_ADD, fd1, &ev);
ev.data.fd = fd2; // 注意这个值必须要指定,不然 epoll_wait 返回了你也不知道是谁发生了事件
ev.events = EPOLLOUT; // 想监听可写事件,因为没有指定 EPOLLET 选项,所以默认是水平触发
epoll_ctl(epfd, EPOLL_CTL_ADD, fd2, &ev);
// 事件数组 evts 用来保存 epoll_wait 返回的事件
struct epoll_event evts[4];
// 开始等待事件发生,res 表示发生了事件的个数
int res = epoll_wait(epfd, evts, 4, -1);
// 开始处理所有事件
for (i = 0; i < res; ++i)
// 这个 fd 就是你一开始通过 event 的 data 成员传进去的。
fd = evts[i].data.fd;
if (evts[i].events & EPOLLIN)
//读文件描述符
if (evts[i].events & EPOLLOUT)
//写文件描述符
2022深度学习开发者峰会
5月20日13:00让我们相聚云端,共襄盛会!
以上是关于slect( )poll( )epoll( )函数详解的主要内容,如果未能解决你的问题,请参考以下文章
I/O多路转接模型 [select] [poll] [epoll]
I/O多路转接模型 [select] [poll] [epoll]