linux总结篇之——I / O复用(select poll epoll)

一、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函数这里,直到被监视的文件描述符一个或多个发生了状态改变。
——用钓鱼的例子来说:以前一人一个鱼竿,只能干等一个;现在一人多个鱼竿,只要是多根鱼竿中的一个或多个有鱼上钩就可以收竿,从而大大提高了钓到鱼(获取到数据)的概率。


缺点:

  1. ※因为使用fd_set位图结构体存储文件描述符,所以能存储的文件描述符有上限,一般是1024
  2. ※每次调用select,都需要把存放文件描述符的集合从用户态拷贝到内核态,开销很大
  3. ※在内核态中,也需要对文件描述符集合进行遍历查找,因为是线性结构,所以时间复杂度是O(n),随着文件描述符的增加,效率会变低
  4. select函数这个接口需要传的参数很多,并且都要先手动设置好后再传入,并且其中的输入输出型参数并未实现输入输出分离,因为还需要用户自己定义一块缓冲区存储原先的数据,从用户使用角度来说并不方便。


二、poll函数

1. 函数参数详解

#include<poll.h>
int poll(struct pollfd *fds, 
		 nfds_t nfds, 
		 int timeout);
  1. 形参: 1 和 3是输入输出型参数
    1).fds:数组,存储pollfd类型结构体
    2).nfds:数组fds的大小
    3).timeout:超时时间,同select 函数的timeout 一样
  2. **返回值:**同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下使用。而其他函数不是。



注意:select、poll、epoll这三个函数都存在拷贝内存的额外开销。(有资料说epoll函数存在内存映射机制,避免了内存开销。这一说法是错误的!!!)

操作系统不可能直接把信息暴露给用户的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值