1.select
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
1)nfds为被监听文件描述符的总数,通常为个数+1
2)struct fd_set由定义来看__fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];包含一个整形数组,其中每个位代表着一个文件描述符,这代表了读文件描述符的集合。需要注意的是,监听文件描述符的最大数由fd_setsize决定,其最大值是1024。
3)代表写文件描述符的集合。
4)异常文件描述符的集合。
5)struct timeout结构
struct timeval { __time_t tv_sec; /* Seconds. */ __suseconds_t tv_usec; /* Microseconds. */ };
第一个成员为秒,第二个成员为微妙,可见提供到微妙的支持。如果该结构指定为null,则一直阻塞此函数直到有事件发生。
6)return:成功返回0,失败返回-1并置errno。
7)提供以下宏函数代替位的繁琐操作
#define FD_SET(fd, fdsetp) __FD_SET (fd, fdsetp) //将文件描述符置位,相当于加入到该集合当中。#define FD_CLR(fd, fdsetp) __FD_CLR (fd, fdsetp) //清除该文件描述符上的位,相当于从集合中取出。#define FD_ISSET(fd, fdsetp) __FD_ISSET (fd, fdsetp) //判断文件描述符是否在其中#define FD_ZERO(fdsetp) __FD_ZERO (fdsetp) //将该文件描述符清零。
8)更要注意的是,在向内核注册事件,通知完该事件之后,一定要重新注册一遍,即fd_set需要重新FD_SET进文件描述符。因为只能监听3个事件,读写异常,所以限制较多。
2.poll
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
1)struct pollfd结构
int fd; /* file descriptor */ short events; /* requested events */ short revents; /* returned events */ };
1.1)events是需要在该文件描述符上监听的事件,通常是下列宏的位掩码:
POLLIN //该文件描述符有数据可读 There is data to read. POLLPRI //紧急数据(oob) 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) //仅linux支持,文件描述符对方关闭了写,或者关闭了连接,这个很有必要知道 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). POLLHUP //挂起 Hang up (output only). POLLNVAL //文件描述符不有效 Invalid request: fd not open (output only). 如果定义了_XOPEN_SOURCE,那么还有如下的宏:
POLLRDNORM //normal,正常的可读数据,相当与pollin
Equivalent to POLLIN.POLLRDBAND //linux中不可用
Priority band data can be read (generally unused onLinux).POLLWRNORM //相当于pollout
Equivalent to POLLOUT.POLLWRBAND //更高优先级的数据可写
Priority data may be written.1.2)内核返回的事件。
2)文件描述符的总数,typeof unsigned long int nfds_t
3)监听时间,指定为0立即返回,指定为-1则一直阻塞直到所监听描述符上有事件发生。
4)return:正常返回0,异常返回-1并置errno
3.epoll
//需要如下的一组函数来完成 * epoll_create(2) creates an epoll instance and returns a file descriptor referring to that instance. (The more recent epoll_cre‐ ate1(2) extends the functionality of epoll_create(2).) * Interest in particular file descriptors is then registered via epoll_ctl(2). The set of file descriptors currently registered on an epoll instance is sometimes called an epoll set. * epoll_wait(2) waits for I/O events, blocking the calling thread if no events are currently available.
1)向内核注册事件表,参数为需要监听的文件描述符的个数。
2)extern int epoll_ctl (int __epfd, int __op, int __fd,struct epoll_event *__event) __THROW; //类似于fcntl函数
2.1)指定的epollfd事件表的描述符
2.2)op操作的类型,有如下的宏可选
#define EPOLL_CTL_ADD 1 /* Add a file descriptor to the interface. */ //添加#define EPOLL_CTL_DEL 2 /* Remove a file descriptor from the interface. */ //删除#define EPOLL_CTL_MOD 3 /* Change file descriptor epoll_event structure. */ //修改
2.3)需要监听的文件描述符
2.4)struct epoll_event结构
struct epoll_event{ uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */} __EPOLL_PACKED;
2.4.1)events为需要监听事件的位掩码,如下所示
enum EPOLL_EVENTS { EPOLLIN = 0x001, //有数据可读#define EPOLLIN EPOLLIN EPOLLPRI = 0x002, //有紧急数据(oob)可读#define EPOLLPRI EPOLLPRI EPOLLOUT = 0x004, //该描述符有缓冲区可供写#define EPOLLOUT EPOLLOUT EPOLLRDNORM = 0x040, //normal#define EPOLLRDNORM EPOLLRDNORM EPOLLRDBAND = 0x080, //优先级数据可读#define EPOLLRDBAND EPOLLRDBAND EPOLLWRNORM = 0x100, //normal write#define EPOLLWRNORM EPOLLWRNORM EPOLLWRBAND = 0x200, //优先级数据可写#define EPOLLWRBAND EPOLLWRBAND EPOLLMSG = 0x400, //没了解过#define EPOLLMSG EPOLLMSG EPOLLERR = 0x008,#define EPOLLERR EPOLLERR //错误消息 EPOLLHUP = 0x010,#define EPOLLHUP EPOLLHUP //挂起 EPOLLRDHUP = 0x2000,#define EPOLLRDHUP EPOLLRDHUP //描述符的对方关闭了写操作,或者关闭了连接 EPOLLWAKEUP = 1u << 29,#define EPOLLWAKEUP EPOLLWAKEUP //没了解 EPOLLONESHOT = 1u << 30,#define EPOLLONESHOT EPOLLONESHOT //在任意时刻仅可以触发一个可读可写异常事件,常用与多线程 EPOLLET = 1u << 31#define EPOLLET EPOLLET //使用et方式工作,默认lt };
2.4.2)
typedef union epoll_data{ void *ptr; int fd; uint32_t u32; uint64_t u64;} epoll_data_t;
可以看出是一个union,ptr指的是数据地址,fd指的是文件描述符,通常我们使用文件描述符。
2.4.3)关于et(边缘敏感)与lt(水平敏感):lt模式:通知之后可不处理。et:通知之后必须处理,且对该事件仅通知一次
2.5)成功返回0,失败返回-1
3)
extern int epoll_wait (int __epfd, struct epoll_event *__events, int __maxevents, int __timeout);
3.1)指定的epollfd
3.2)触发事件的数组集合
3.3)需要监听的最大描述符数量
3.4)timeout,-1则一直阻塞直到有事件发生。
3.5)返回有发生事件的文件描述符个数。
4)通常我们要往一个epollfd添加文件描述符的函数接口可以这么写:
void addfd(int epollfd,int fd){ struct epoll_event event; event.data.fd=fd; event.events=EPOLLIN|EPOLLET; epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event); setnonblocking(fd);}
5)通常设置一个文件描述符为非阻塞可以这么写:
int setnonblocking(int fd){ int old_option=fcntl(fd,F_GETFL); int new_option=old_option|O_NONBLOCK; fcntl(fd,F_SETFL,new_option); return old_option;}
4.详情请看man或者源码