select和pselect
头文件
#include <sys/select.h>
函数定义
int select(int n,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * timeout);
int pselect(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask);
int maxfdp
一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。
fd_set *readfds
指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。
fd_set*writefds
是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。
fd_set *errorfds
同上面两个参数的意图,用来监视文件错误异常。
struct timeval *timeout
struct timespec *timeout
是select的超时时间,这个参数至关重要,它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。
sigset_t *sigmask
选择的信号屏蔽字。若sigmask为空,那么在与信号有关的方面,pselect的运行状况和select相同。否则,sigmask指向一信号屏蔽字,在调用pselect时,以原子操作的方式安装该信号屏蔽字。在返回时恢复以前的信号屏蔽字。
struct fd_set 结构体的基本操作
FD_ZERO(fd_set *) 清空集合
FD_SET(int ,fd_set*)将一个给定的文件描述符加入集合之中
FD_CLR(int,fd_set*)将一个给定的文件描述符从集合中删除
FD_ISSET(int ,fd_set* )检查集合中指定的文件描述符是否可以读写
struct timeval结构体
struct timeval{
long tv_sec; 秒
long tv_usec; 微妙
};
struct timespec
{
time_t tv_sec;秒
long tv_nsec; 纳秒
};
区别
1.select超时值用timeval指定,但pselect用timespec指定。timeval是秒和微妙,timespec是秒和纳秒所以timespec提供了比timeval更准确的超时时间。
2.pselect的超时值被声明为const,这就保证调用pselect不会改变此值。
3.pselect可以在函数等待期间屏蔽指定的信号,不被信号打断。
poll和ppoll(linux特有)
头文件
#include <poll.h>
函数定义
int poll(struct pollfd *fds,nfds_t nfds, int timeout);
int ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout, const sigset_t *sigmask);
struct pollfd *fds
是一个struct pollfd结构类型的数组,用于存放需要检测其状态的Socket描述符;每当调用这个函数之后,系统不会清空这个数组,操作起来比较方便;特别是对于 socket连接比较多的情况下,在一定程度上可以提高处理的效率;这一点与select()函数不同,调用select()函数之后,select() 函数会清空它所检测的socket描述符集合,导致每次调用select()之前都必须把socket描述符重新加入到待检测的集合中;因此,select()函数适合于只检测一个socket描述符的情况,而poll()函数适合于大量socket描述符的情况
nfds_t nfds
标记数组fds中的结构体元素的总数量
int timeout
poll函数调用阻塞的时间,单位:毫秒
struct timespec *timeout
看上文介绍
const sigset_t *sigmask
看上文介绍
strust pollfd 结构体
结构体定义
typedef struct pollfd {
int fd; /* 需要被检测或选择的文件描述符*/
short events; /* 对文件描述符fd上感兴趣的事件 */
short revents; /* 文件描述符fd上当前实际发生的事件*/
} pollfd_t;
events和revents描述事件
POLLIN 有数据可读
POLLRDNORM 有普通数据可读
POLLRDBAND 有优先数据可读
POLLPRI 有紧迫数据可读
POLLOUT 写数据不会导致阻塞
POLLWRNORM 写普通数据不会导致阻塞
POLLWRBAND 写优先数据不会导致阻塞
POLLMSGSIGPOLL 消息可用
epoll
头文件
#include <sys/epoll.h>
函数定义
int epoll_create(int size);创建一个epoll
int size:告诉内核监听的数目
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);epoll事件注册函数
int epfd:epoll的句柄
int op:表示动作,用3个宏来表示:
int fd:需要监听的标示符EPOLL_CTL_ADD(注册新的fd到epfd),
EPOLL_CTL_MOD(修改已经注册的fd的监听事件),
EPOLL_CTL_DEL(从epfd删除一个fd)
struct epoll_event *event:告诉内核需要监听的事件
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队列里
typedef union epoll_data {void *ptr;int fd;__uint32_t u32;__uint64_t u64;} epoll_data_t;
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);等待事件的产生
int epfd:epoll的句柄
struct epoll_event * events:等待事件的头指针
int maxevents:等待时间最大数,不能大于创建epoll_create()时的size
int timeout:超时时间(毫秒,0会立即返回,-1将永久阻塞)
参考:http://blog.youkuaiyun.com/xiajun07061225/article/details/9250579