一 select 系统调用
#include <sys/select.h>
int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds,
struct timeval* timeout );
1) nfds 参数指定被监听的文件描述符的总数,它通常被设置为select监听的所有文件描 述符中的最大值加1,因为文件描述符是从0开始计数的。
2) readfds、writefds和exceptfds参数分别指向可读,可写和异常等事件对应的文件 描述符集合。应用程序调用select函数时,通过这3个参数传入自己感兴趣的文件描述符。 select 调用返回时,内核将修改它们来通知应用程序哪些文件描述符已经就绪。这3个参 数是fd_set 结构指针类型。fd_set 结构体仅包含一个整型数组,该数组的每个元素的每 一个位(bit)标记一个文件描述符。fd_set能容纳的文件描述符数量由FD_SETSIZE指定, 这就限制了select能同时处理的文件描述符的总量。
/* 1024个文件描述符*/
#define __FD_SETSIZE 1024
3) timeout 参数用来设置select函数的超时时间,它是一个timeval的结构类型的指针, 采用指针参数是因为内核将修改它以告诉应用程序select等待了多久。这个timeout在调用 失败的时候timeout值不确定。
struct timeval
{
long tv_sec; /*秒数*/
long tv_usec; /*微秒数*/
} 如果给timeout俩成员都传递0,则select将立即返回。如果给timeout传递NULL,则select 将一直阻塞,直到某个文件描述符就绪。
返回值:
select 成功时候返回就绪(可读、可写、异常) 文件描述符的总数。
如果在超时时间内没 有任何文件描述符就绪,select将返回0。
select 失败时返回-1 并设置errno。
如果在select等待期间,程序接收到信号,则select立即返回-1,并设置errno为EINTR。
2 文件描述符就绪条件
- socket内核接受缓存区中字节数大于或等于其低水位标记SO_RCVLOWAT。此时可以无阻塞的读该socket,并且读操作返回的字节数大于0。
- socket 通信的对方关闭连接。此时对该socket的读操作将返回0。
- 监听socket上有新的连接请求。
- socket上有未处理的错误。此时我们可以使用getsocket来读取和清除该错误。
- socket内核发送缓冲区中的可用字节数大于或等于其低水位标记SO_SNDLOWAT。此时可以无阻塞的写该socket,并且写操作返回的字节数大于0。
- socket的写操作被关闭。对写操作被关闭的socket执行写操作,将触发一个SIGPIPE信号。
- socket使用非阻塞connect连接成功或者失败(超时)之后。
- socket上有未处理的错误。此时我们可以使用getsocket来读取和清除该错误。
二 poll系统调用
#include <poll.h>
int poll( struct pollfd* fds, nfds_t nfds, int timeout );1) fds 参数是一个pollfd结构类型的数组,它指定所有我们感兴趣的文件描述符上发生的 可读、可写和异常等事件。
struct pollfd
{
int fd ; /*文件描述符*/
short events; /*注册的事件*/
short revents; /*实际发生的事件,由内核填充*/
} 其中,fd成员指定文件描述符:events成员告诉poll监听fd上的哪些事件,它是一系列事件 按位或;revents成员则由内核修改,以通知应用程序fd上实际发生了哪些事件。
poll事件 支持的类型:
常量 | 说明 |
POLLIN | 普通或优先级带数据可读 |
POLLRDNORM | 普通数据可读 |
POLLRDBAND | 优先级带数据可读(linux不支持) |
POLLPRI | 高优先级数据可读 |
POLLOUT | 普通数据、优先数据可写 |
POLLWRNORM | 普通数据可写 |
POLLWRBAND | 优先级带数据可写 |
POLLRDHUP | tcp连接被对方关闭,或者对方关闭了写操作,它由GNU引入 |
POLLERR | 发生错误 |
POLLHUP | 发生挂起。比如管道的写端被关闭后,该读端描述符上将收到POLLHUP事件 |
POLLNVAL | 文件描述符没有打开 |
2) nfds 参数指定被监听事件集合的fds的大小。 typedef unsigned long int nfds_t;
3) timeout 参数指定poll的超时值,单位是毫秒。当timeout为-1时,poll调用将永远阻塞 直到某个事件发生;当timeout为0时,poll调用将立即返回。 poll 系统调用的返回值的含义与select相同。
三 epoll 系列系统调用
#include <sys/epoll.h>int epoll_create( int size )#include <sys/epoll.h>
int epoll_ctl( int epfd, int op, int fd, struct epoll_event *event ) fd 参数是要操作的文件描述符,op参数则指定操作类型。
操作类型有如下3种:
- EPOLL_CTL_ADD 往事件表中注册fd上的事件。
- EPOLL_CTL_MOD 修改fd上的注册事件。
- EPOLL_CTL_DEL 删除fd上的注册事件。
event 参数指定事件,它是epoll_event 结构指针类型。
struct epoll_event
{
__unit32_t events; /*epoll事件*/
epoll_data_t data; /*用户数据*/
}; 其中events成员描述事件类型。epoll支持的事件类型和poll基本相同。表示epoll事件类型 的宏是在poll对应的宏上加“E”,比如epoll的数据可读事件是EPOLLIN。但epoll有两个额外 的事件类型--EPOLLET和EPOLLONESHOT。它们对于epoll的高效运作非常关键。
data成员用 于存储用户数据,其类型epoll_data_t的定义如下:
typedef union epoll_data
{
void* ptr;
int fd;
unit32_t u32;
unit64_t u64;
} epoll_data_t;epoll_data_t 是一个联合体,其4个成员中使用最多的是fd,它指定事件所属的目标文件描 述符。
ptr 成员可用来指定与fd相关的用户数据。但由于epoll_data_t 是一个联合体,我们不能 同时使用其ptr成员和fd成员,如果要将文件描述符和用户数据关联起来,以实现快速的数据 访只能使用其他手段,比如放弃使用epoll_data_t的成员,而在ptr指向的用户数据中包含 fd epoll_ctl成功时返回0,失败则返回-1并设置errno。
# include <sys/epoll.h>
int epoll_wait( int epfd, struct epoll_event* events, int maxevents, int timeout) 该函数成功时返回就绪的文件描述符的个数,失败时返回-1,并设置errno。
参数:
timeout:参数指定epoll_wait的超时值,单位是毫秒。
当timeout为-1时,epoll_wait调用将永远阻塞,直到某个事件发生;
当timeout为0时,epoll_wait调用将立即返回。
maxevents: 参数指定最多监听多少个事件,它必须大于0。
epoll_wait 函数如果检测到事件,就将所有的就绪事件从内核事件表(由epfd参数指定) 复制到它的第二个参数events 指向的数组中。
这个数组只用于输出epoll_wait检测到的就 绪事件,这就极大的提高了应用程序索引就绪文件描述符的效率。