select
1.原函数
#include <sys/select.h> //需要包含的头文件
#include <sys/time.h> //由于最后一个参数所以也要包含此头文件
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, const struct timeval *timeout);
其作用是进程使用该函数让内核等待多个事件种的任何一个发生,并且在只有一个或者多个事件发生或者经历指定的一段时间后再唤醒进程。
2.返回值
返回值类型是int,若有就绪文件描述符则返回其数量,若超时则返回0,出错则返回-1。
3.参数
(1)timeout
timeout参数指定等待所制定描述符中任意一个就绪可以花多长时间,其类型是指向timeval结构体的指针
struct timeval {
long tv_sec; //秒 s
long tv_usec; //微秒 ns
//1s = 1000ms = 1000000ns
}
不同情况下的参数设置:
- 设为空指针: 永远等待下去(阻塞监听)
- 设为一个固定值(单位是秒或者微秒):等待一个固定时间,这个时间不超过所指定的参数
- 设为0:非阻塞轮询(不等待,检查描述符后立即返回)。
(2)readfds、writefds、exceptfds
这三个参数的类型是fd_set,意思是一个描述符集,通常用整数数组来表示。
创建一个描述符集后有一件很重要事就是初始化,因为自动变量分配会导致描述符集中有不存在的描述符而产生不可预期的错误。
fd_set fdset;
FD_ZERO(&fdset); //初始化 {0, 0, 0, 0, 0, 0, ....}
//如果要打开描述符为1、3、5的对应位:
FD_SET(1, &fdset); // 数组变为{0, 1, 0, 0, 0, 0, ...}
FD_SET(3, &fdset); // 数组变为{0, 1, 0, 1, 0, 0, ...}
FD_SET(5, &fdset); // 数组变为{0, 1, 0, 1, 0, 1, ...}
对于这个描述符集的操作有四个函数:
void FD_CLR(int fd, fd_set *set) //在描述符集中关闭指定描述符的比特位,也就是置0
int FD_ISSET(int fd, fd_set *set) //判断指定描述符是否在描述符集中(在返回1,不在返回0)
void FD_SET(int fd, fd_set *set) //在描述符集中打开指定描述符的比特位,也就是置1
void FD_ZERO(fd_set *set) //清除所有比特位,相当于全部置0(初始化)
如果这三个参数对于哪个事件不感兴趣,则可以设置为空指针,但是若都设为空指针,它就变成了一个比sleep()更为精确的计时器,毕竟它的最小的单位是微秒。
(3)nfds
第一个int类型的参数nfds指定待测试的描述符的个数。(ps:描述符是从0开始的)
4.优缺点
优点:能够完成跨平台的文件描述符监听。
缺点:监听上限受文件描述符集合限制,最大1024
poll
1.原函数
#include <poll.h>
int poll(struct pollfd *fds, nfds_t ndfs, int timeout);
它的作用与select相似,但相比select,poll使用链表保存文件描述符。
2.返回值
- -1:出现错误。
- 0:定时器到时之前若没有已预备的描述符。
- 大于0的整数:就绪的描述符的个数。
3.参数
(1) fds
struct pollfd {
int fd; //待监听的文件描述符
short events; //一个传入参数,待监听的文件描述符的请求事件
short revents; //一个传出参数,待监听的文件描述符的请求事件确实发生后,内核会通过这个参数返回一个值
}
其中带监听的文件描述符所对应的监听事件events可以选择的参数为:
- POLLIN:表示有数据要读
- POLLOUT:表示立即写但不会阻塞
- POLLPRI:表示有紧急的数据要读,比如TCP socket上的带外数据,
输出参数revents表示监听的事件发生的情况,其返回值有三种:
- POLLERR:出错
- POLLHUP:事件被挂起
- POLLNVAL:无效的请求(fd无法打开)
如果我们不再关心某个特定的描述符,那么可以把它对应的pollfd结构体内的fd改为负值,poll()在执行时会忽略fd为负值的描述符的请求事件(events),返回时将其revents置0。
(2)nfds
其用来指定fds数组中实际监听的fd的个数,并非数组的大小。
(3)timeout
不同情况下的参数设置:
- 设为大于0的整数:等待指定时间的毫秒数。
- 设为负数:若指定的参数为负数,则表示无限等待。如果系统无法提供毫秒级别的计时器,则向上取值。
- 设为0:即使现在还没有文件描述符准备好,也不继续监听了,立即返回。