select、poll、epoll的用法总结

来源:互联网 发布:控制射精 知乎 编辑:程序博客网 时间:2024/06/06 00:14
/*select*/ #include <sys/select.h>#include <sys/time.h>#include <sys/types.h>#include <unistd.h>int select(int nfds, fd_set *readfds, fd_set *writefds,  fd_set *exceptfds, struct timeval *timeout);void FD_CLR(int fd, fd_set *set);int  FD_ISSET(int fd, fd_set *set);void FD_SET(int fd, fd_set *set);void FD_ZERO(fd_set *set); struct timeval {               long    tv_sec;         /* seconds */               long    tv_usec;        /* microseconds */           };----------------------------------------------------------------------/* 功能:io多路复用函数参数:nfds -> 函数中监视的最大文件描述符加1:readfs->监视可读的文件描述符集合(置为NULL,表忽略可读事件 ):writefs->监视可写的文件描述符集合(置为NULL,表忽略可写事件 ):exceptfs->监视异常的文件描述符集合(置为NULL,表忽略异常事件 ):timeout->超时时间,毫秒,微妙(置为NULL,不管监视的事件是阻塞或非阻塞描述符,只要select的readfds事件没有一个发生,则永久阻塞 ,有任意一个发生,轮询完毕,退出。置为timeout= {.tv_sec = 0,.tv_usec = 0.};则相当于非阻塞))返回值:=0 等待超时,没有事件发生:>0 已改变状态的个数:=-1 出错----------------------------------------------------------------------void FD_CLR(int fd, fd_set *set);参数:fd ->要清除的监视事件的文件描述符: set->当前监视事件集合int  FD_ISSET(int fd, fd_set *set);参数:fd ->判断文件描述符fd 是否在set集合内(select 会清除事件集合中没有发生的事件): set->当前监视事件集合返回值:1 ->成功,有此事件0-> 失败,无此事件void FD_SET(int fd, fd_set *set);参数:fd ->要加入的监视事件的文件描述符: set->当前监视事件集合void FD_ZERO(fd_set *set);参数:set-> 清除set集合的所有事件----------------------------------------------------------------------注意:循环调用select函数前,每次都要先初始化时间值和监视事件集合(select函数执行后,监视事件集合就只剩就绪的事件,没就绪的事件已经清除出事件集合)select() 实际运行方式是 在超时范围内一直轮询,一旦有一个事件发生,则立即退出轮询。所以,实际上一次轮询只有一个事件可以被处理。select() 以及poll()、epoll()监视的文件描述符如果是阻塞的,可能造成select之后,在执行阻塞事件的时候,如果又没有就绪,则一直阻塞在那,丧失select的作用。例如:轮询accept()就绪,但在执行时又阻塞。在linux/posix_types.h头文件有这样的声明:#define __FD_SETSIZE    1024表示select最多同时监听1024个fd,当然,可以通过修改头文件再重编译内核来扩大这个数目,但这似乎并不治本。缺点:函数并没有告知我们到底发生了什么事件,用户就需要轮询整个集合。如果监视事件太多,效率慢优点:事件集合是动态变化。遇到的问题:在网络套接字处理中,select要监测的读/写套接字异常退出了(服务端套接字连接的客户端进程关闭,这时如果服务端的套接字还在select监测读写中,select则卡死在那,什么也监测不到)这就要求必须把异常的描述符FD_CLR马上清理出去,否则下一次轮询的select不能继续工作。*/========================================================================================/*poll*/#include <poll.h>int poll(struct pollfd *fds, nfds_t nfds, int timeout);   struct pollfd {   int   fd;         /* file descriptor */   short events;     /* requested events */   short revents;    /* returned events */   };----------------------------------------------------------------------/* 功能:io多路复用函数参数:fds->监视事件的集合结构体首地址struct pollfd fds[nfds] = {[0] = {.fd = (要监视对象的文件描述符),.events = (监测的事件),.revents = (监测的事件events发生,则revents = events),},[1]= {......},......};:nfds-> 结构体的个数(事件的个数):timeout->超时时间(毫秒)返回值:=0 等待超时,没有事件发生:>0 已改变状态的个数:=-1 出错 ------------------------------------------------------------------------events 可选事件:(例如,events = POLLIN|POLLOUT,则监视读和写两种事件)POLLIN There is data to read.POLLPRI There is urgent data to read (e.g., out-of-band  data  on TCP socket; pseudoterminal master in packet mode has seen state change in slave).POLLOUT Writing now will not block.POLLRDHUP (since Linux 2.6.17) Stream socket peer closed connection, or shut down  writ‐ ing  half  of  connection.   The _GNU_SOURCE feature test macro must be defined (before including any header files) in order to obtain this definition.POLLERR Error condition (output only).POLLERR Error condition (output only).POLLHUP Hang up (output only).POLLNVAL Invalid request: fd not open (output only).----------------------------------------------------------------------注意:用户定义的结构体事件集合要先初始化值,例如:{fd =-1,event = 0, revent = 0}。调用poll函数,函数判断每个事件,发生则设置revents变量,否则,清除revents变量。用户不必手动清除revents变量事件。用户遍历结构体数组,查找什么事件发生。例如:if(event[i].revents & POLLIN){ ...}.poll() 和select()、epoll() 实际运行方式是 在超时范围内一直轮询,一旦有一个事件发生,则立即退出轮询。所以,实际上一次轮询只有一个事件可以被处理。select() 以及poll()、epoll()监视的文件描述符如果是阻塞的,可能造成select之后,在执行阻塞事件的时候,如果又没有就绪,则一直阻塞在那,丧失select的作用。一般把文件描述符设置为非阻塞O_NONBLOCK.例如:轮询accept()就绪,但在执行时又阻塞。特例:当对方关闭连接、 EPOLLERR,都可以认为是一种EPOLLIN事件发生,在read的时候分别有0,-1两个返回值。====================================================================/*epoll*/#include <sys/epoll.h>       int epoll_create(int size);描述:创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。返回值:      (成功则返回一个非负特殊文件描述符值,失败返回-1)-----------------------------------------------------------------------------------int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);描述:epoll的事件注册函数,第一个参数是 epoll_create() 的返回值,第二个参数表示动作,使用如下三个宏来表示EPOLL_CTL_ADD    //注册新的fd到epfd中;EPOLL_CTL_MOD    //修改已经注册的fd的监听事件;EPOLL_CTL_DEL    //从epfd中删除一个fd;第三个参数是需要监听的fd,第四个参数是告诉内核需要监听什么事,struct epoll_event 结构如下: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     //表示对应的文件描述符可以读(包括对端SOCKET正常关闭);EPOLLOUT    //表示对应的文件描述符可以写;EPOLLPRI    //表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);EPOLLERR    //表示对应的文件描述符发生错误;EPOLLHUP    //表示对应的文件描述符被挂断;EPOLLET     //将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。EPOLLONESHOT//只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,//需要再次把这个socket加入到EPOLL队列里。特别注意:当对方关闭连接、 EPOLLERR,都可以认为是一种EPOLLIN事件发生,在read的时候分别有0,-1两个返回值。--------------------------------------------------------------------------------------int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);描述:参数events为用户新建的结构体数组用来存储从内核得到事件的集合,maxevents 告之内核这个events有多大,这个 maxevents 的值不能大于创建 epoll_create() 时的size,参数 timeout 是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。该函数返回需要处理的事件数目,如返回0表示已超时。poll() 和select()、epoll() 实际运行方式是 在超时范围内一直轮询,一旦有一个事件发生,则立即退出轮询。所以,实际上一次轮询只有一个事件可以被处理。select() 以及poll()、epoll()监视的文件描述符如果是阻塞的,可能造成select之后,在执行阻塞事件的时候,如果又没有就绪,则一直阻塞在那,丧失select的作用。一般把文件描述符设置为非阻塞O_NONBLOCK.例如:轮询accept()就绪,但在执行时又阻塞。

0 0