select
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
参数说明
返回值
- 返回值>0 表示成功返回可访问的文件描述符个数,
- 返回值==0 表示标识等待时间到期
- 返回值<0 表示出现错误
int nfds
当前最大文件描述符值+1:(2,3,5,6,10)那么nfds=10+1,这个是在select系统函数内需要遍历的最大值。
fd_set *readfds,fd_set *writefds,fd_set *exceptfds
读文件描述符集,写文件描述符集,异常文件描述符集
- fd_set类型本质是一个位图,位图的位置 表示 相对应的文件描述符,内容表示该文件描述符是否有效,1代表该位置的文件描述符有效,0则表示该位置的文件描述符无效。
- 如果将文件描述符2,3设置位图当中,则位图表示的是为1100。
- fd_set的上限是1024个文件描述符。
设置函数有:
FD_SET(fd,fdset); //设置一个fd到fdset中进行等待
FD_ZERO(fdset); //取消一个fd到fdset中的等待
FD_CLR(fd,fdset); //清除该fdset中的所有fd
FD_ISSET(fd,fdset) //判定该文件描述符是否存在于fdset集
但是在这个select函数中,这3个参数包括后面的timeout都属于,输入输出型参数。在select类型的服务器中,我们必须要每次在执行select函数前需要从头开始设置每个fdset类型,因为每次函数返回后都会修改传入的fdset参数。
struct timeval *timeout
- NULL/nullptr:select调用后进行阻塞等待,直到被监视的某个文件描述符上的某个事件就绪。
- 0(这个0可不是NULL而是 timeval{0,0}):selec调用后进行非阻塞等待,无论被监视的文件描述符上的事件是否就绪,select执行后都会立即返回,不会阻塞等待。
- 特定的时间值:select调用后在指定的时间内进行阻塞等待,如果被监视的文件描述符上一直没有事件就绪,则在该时间后select进行超时返回。
struct timeval
{
__time_t tv_sec; /* Seconds. */ 秒
__suseconds_t tv_usec; /* Microseconds. */微秒
};
#define __time_t long
#define __suseconds_t long
函数说明
调用函数,他会管理设置在3个fds中的所有文件描述符,只要其中文件描述符中允许写入或者有数据可读取,返回并且设置这3个fds,以输出型参数方式返回给上层。在函数返回后,拿着这3个参数使用FD_ISSET判定那些fd允许访问。
select的优点与缺陷
优点:
- 效率高 一个执行流同时等待多个fd等待。
- 应用场景:存在大量的链接,但是只有少量的是活跃的!节省资源 如果是多线程/进程需要pcb等内存资源
缺点:
- 为了维护第三方数组,select服务器会充满大量的遍历,OS底层帮我们关心fd的时候,也要遍历
- 每一次都要对select输出参数进行重新设定
- 能够同时管理的fd的个数是有上 限
- 因为几乎每一个参数都是输入输出型的,select一定会频繁的进行用户到内核,内核到用户的参数数据拷贝
- 编码比较复杂
poll
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
参数说明
返回值
- 返回值小于0, 表示出错;
- 返回值等于0, 表示poll函数等待超时;
- 返回值大于0, 表示fds数组有多少事件可以访问;
struct pollfd *fds
fds是一个结构体数组
struct pollfd
{
int fd; //需要观察的文件描述符
short int events; //输入时的参数
short int revents; //输出时的参数
};
注意,这里的指针并不意味是只有一个pollfd,这里的指针是可以看成pollfd[NUM]的一个数组的,这个数组大小并不受约束,并且我们在堆创建,允许扩容realloc,每一个元素都是对应着一个文件描述符,管理着一个文件描述符。
int nfds
标识*fds指向的数组有多少个元素,用于poll函数轮询使用
int timeout
最后一个参数timeout:是一个用毫秒表示的时间,是指定poll在返回前没有接收事件时应该等待的时间。
- timeout > 0 : 等待timeout毫秒
- timeout==0 :不会阻塞
- timeout < 0 :阻塞等待
函数说明
poll与select相比,poll的编写较为简单。因为poll它有一个结构体,去管理关心事件,不需要像select还得自己去写第三方数组。其次poll它没有数量限制,select是数量限制的,但是数量大了必然会影响效率,poll和select在底层都是采用轮询检测的方式去查看要关心的fd的事件是否就绪。
poll的优缺点
优点
- 效率高
- 有大量的连接,但是只有少量的是活跃的。节省资源
- 输入输出参数分离的,不需要进行大量的重置
- poll参数级别,没有可以管理的fd的上限
缺点
- poll依旧需要不少的遍历,在用户层检测时间就绪,与内核检测fd就绪,都是一样->epoll出现
- poll需要内核到用户的拷贝
- poll的代码也比较复杂 -- 比select容易