select:
1.select:当被监听的 fd(文件描述符)就绪后会返回,但是我们无法知道具体是哪些 fd 就绪了,只能遍历所有的 fd。通常来说某一时刻,就绪的 fd 并不会很多,但是使用 select 必须要遍历所有的 fd,这就造成了一定程度上的性能损失。select 最多可监听的 fd 是有限制的,32位操作系统默认1024个,64位默认2048
select函数的API:
select函数的API
#include <sys/select.h>
/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
功能: 监听多个文件描述符的属性变化(读,写,异常)
void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);参数:
nfds : 最大文件描述符+1
readfds : 需要监听的读的文件描述符存放集合
writefds :需要监听的写的文件描述符存放集合 NULL
exceptfds : 需要监听的异常的文件描述符存放集合 NULL
timeout: 多长时间监听一次 固定的时间,限时等待 NULL 永久监听
struct timeval {
long tv_sec; /* seconds */ 秒
long tv_usec; /* microseconds */微妙
};返回值: 返回的是变化的文件描述符的个数
注意: 变化的文件描述符会存在监听的集合中,未变化的文件描述符会从集合中删除
select实现原理:
应用层中父进程通过内核的selsect监听文件描述符缓冲区的变化,内核就会返回给父进程
以fd_set为例,每次都要从用户态拷贝至内核态,同时还要在内核态进行循环遍历,然后把有事件的响应的文件描述符fd_set返回,又要从内核态拷贝至用户态。用户态拿到这个有事件的文件描述符返回,还要针对返回的描述符进行遍历,才能知道哪个文件描述符对应的Socket可写可读,总共经历了两次遍历,两次拷贝,所以说为什么Select在文件描述符比较多的情况,效率为什么是低下的原因。
select 的优缺点:
优点: 跨平台
缺点:
文件描述符1024的限制 由于 FD_SETSIZE的限制
只是返回变化的文件描述符的个数,具体哪个那个变化需要遍历
每次都需要将需要监听的文件描述集合由应用层符拷贝到内核
大量并发,少了活跃,select效率低