文章目录
一、select 函数
1. 函数功能分析
系统调用 select函数用来实现多路复用输入 / 输出模型,让我们的程序监视多个文件描述符的状态变化。
程序会停在select这里等待,直到被监视的文件描述符有一个或多个发生了状态改变
2. 函数参数详解
#include <sys/select.h>
int select(int fds,
fd_set *readfds,
fd_set *writefds,
fd_set *exceptfds,
struct timeval *timeout
);
1.形参(后四个参数均是输入输出型参数):
1)nfds:
被监听的文件描述符的总数,值一般设为所有文件描述符中最大的值+1
2readfds:
位图,每个值为1
的位,代表用户想让操作系统监听(是否具有读事件发生)
的文件描述符
3.writefds:
位图,每个值为1
的位,代表用户想让操作系统监听(是否具有写事件发生)
的文件描述符
4.exceptfds:
位图,每个值为1
的位,代表用户想让操作系统监听(是否具有异常事件发生)
的文件描述符
5.timeout:(为timeval结构)
超时时间,代表select函数将要等待的时间,也是一个输入输出型参数
2.返回值:
select()> 0: 已有事件发生的文件描述符的个数
,从位图上看就是fd_set结构体中已经被内核置为1的位数
select()= 0: 超时
(指文件描述符的状态改变前已经超出timeout时间,没有返回。)
select()=-1: 出错
(错误原因存于errno)
3. 函数用到的结构体fd_set 和 fd_array 和timeval结构
4. 操作 fd_set类型结构体的接口
void FD_CLR(int fd , fd_set *set);
//用来清除set中相关fd的位(将fd置 0)
int FD_ISSET(int fd , fd_set *set);
//用来测试set中相关fd的位是否存在(即是否为1)
void FD_SET(int fd , fd_set *set);
//用来设置set中相关的fd的位,(置1)
void FD_ZERO(fd_set *set);
//用来清除set的全部位(置0)
5.select函数的执行过程
6.select函数的优缺点:
优点:(多路转接)
select函数是让我们的程序监视多个文件描述符的状态变化的,程序会阻塞在select函数这里,直到被监视的文件描述符一个或多个发生了状态改变。
——用钓鱼的例子来说:以前一人一个鱼竿,只能干等一个;现在一人多个鱼竿,只要是多根鱼竿中的一个或多个有鱼上钩就可以收竿,从而大大提高了钓到鱼(获取到数据)的概率。
缺点:
※因为使用fd_set位图结构体存储文件描述符,所以能存储的文件描述符有上限,一般是1024
※每次调用select,都需要把存放文件描述符的集合从用户态拷贝到内核态,开销很大
※在内核态中,也需要对文件描述符集合进行遍历查找,因为是线性结构,所以时间复杂度是O(n),随着文件描述符的增加,效率会变低
- select函数这个接口需要传的参数很多,并且都要先手动设置好后再传入,并且其中的输入输出型参数并未实现输入输出分离,因为还需要用户
自己定义一块缓冲区存储原先的数据
,从用户使用角度来说并不方便。
二、poll函数
1. 函数参数详解
#include<poll.h>
int poll(struct pollfd *fds,
nfds_t nfds,
int timeout);
- 形参: 1 和 3是输入输出型参数
1).fds:
数组,存储pollfd类型结构体
2).nfds:
数组fds的大小
3).timeout:
超时时间,同select 函数的timeout 一样- **返回值:**同select一样
2. 函数用到的两个结构体pollfd 和 fds
##3.pollfd类型的结构体中 events 和 revents 的取值
POLLIN
: 数据可读(包括普通文件数据和优先数据),可作为输入,也可作为输出
POLLOUT
:数据可写(包括普通数据和优先数据),可作为输入,也可作为输出
POLLERR
:错误,不可作为输入,可作为输出
4.poll函数的优缺点
优点:(在select的基础上有所改进)
①取消了能储存的文件描述符的上限 (poll 使用的是数组,select 使用的是位图)
②
实现了输入输出参数的分离 (因为使用了events存储“用户注册的事件”(用户手动设置), 使用了revents 存储“已经发生了的事件”(内核填充))
**缺点:**同select函数的其他缺点一样
三、epoll函数
1. epoll函数的三个函数调用(三部曲)
①-int epoll_create(int size);
——创建一个epoll 函数的事件表
形参:
size:
表示想让内核知道创建一个事件表
需要多大的空间
返回值:成功:返回创建的事件表的文件描述符; 失败:返回 -1
②-int epoll_ctr(int epfd, int op,int fd,struct epoll_event *event);
——epoll的事件注册函数(用户告诉内核你帮我关心哪些文件描述符上的哪些事件)
形参:
1.epfd:
epoll_create 的返回值,事件表文件描述符
2.op:
要执行的操作(3个选项)
EPOLL_CTL_ADD:
注册新的fd到epfd中;(给fd 文件描述符添加event事件)
EPOLL_CTL_MOD:
修改已经注册的监听事件;(将fd文件描述符上的事件修改成event事件)
EPOLL_CTL_DEL:
从epfd中删除一个fd;(删除文件描述符上的事件,将event 参数写NULL)
3.fd:
需要监听的fd(想要操作事件的文件描述符)
4.event
(告诉内核想要监听什么事)该结构体包括两个成员:
**①events
取值 和 poll 函数中的pollfd结构体的取值一样,只不过是在他的前面加上‘E’ **
②data
该联合体有四个成员,最常用到int fd
成员,存储事件所属的文件描述符id
③-int epoll_wait(int epfd,struct epoll_event *events,int maxevents,int timeout);
——收集在epoll监控的事件中已发送的事件
(内核告诉用户你让我关心的哪些文件描述符上的哪些事件已就绪)
1. 形参:
①epfd:
epoll_create 的返回值,事件表文件描述符
①events:
用于存储事件表中所有已发生的事件,即输出型参数,它使得 epoll大大提高了效率
③maxevents:
最多可监听事件的数量
④timeout:
同poll 和 select 的timeout参数
2. 返回值:同select一样
2. epoll 三个函数所用到的结构体和事件表
3. epoll_create函数创建的一个事件表的内部结构
4. epoll函数的优缺点
1. 优点:
①文件描述符无上限,底层使用红黑树存储,来一个插一个。
②红黑树保证了查找的效率,时间复杂度是O(lg n)
③**因为回调机制使得就绪队列中存储的都是 已就绪的事件 的文件描述符,
从而保证了epoll_wait 函数返回时带出的events 数组中全部都是已就绪的文件描述符。**
——适用于“链接多、活跃少”的场景,
比如:QQ ,游戏的创房机制等场景
2.缺点:几乎没有缺点,非说不可的话就是,epoll是linux平台下的函数,只能在linux下使用。而其他函数不是。