IO多路复用

TCP

  • 第一次握手:建立连接,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号
  • 第二次握手:服务器收到syn包,必须确认客户端的syn(ack=j+1),同时自己也发送一个syn包(syn=k),即SYN+ACK包,此时服务端进入SYN_RECV状态;
  • 第三次握手:客户端接收到服务端的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态(TCP连接成功)

Socket套接字

  • socket起源于UNIX,在Unix中万物皆文件的思想下,socket是一种打开 打开 - 读/写 - 关闭 模式的实现,可以把它看成是一种“文件”。
  • socket套接字的结构体就保存着地址信息需要的ip和端口号

epoll网络模型:

概述:

epoll是在2.6内核中提出的,是之前的select和poll的增强版本。相对于select和poll来说,epoll更加灵活,没有描述符限制。epoll使用一个文件描述符管理多个文件描述符,将用户关系的文件描述符的事件存放到内核中的一个事件表中,这样用户态和内核态的copy只需要一次。
设想一个场景:如果有100万个用户同时连接进程,而每个时刻只有几十个或者几百个连接是活跃的,也就是说在每一个时刻进程值需要处理着100万个连接中的一小部分。那么,如何才能做到高效的处理这种场景呢?
这里有一个十分明显的问题,即在某一时刻,进程收集有事件的处理时,其中大部分的连接都是没有事件发生的。如果每次收集事件时,都把100万个连接的套接字传给操作系统(这首先是用户态到内核态的大量复制),而由操作系统内核寻找这些连接中有没有未处理的事件,这将会是巨大的资源浪费。然而select和poll就是这样做的,因此他们最多只能处理几千个并发的连接,而epoll不是,它在Linux内核中申请创建了一个简易的文件系统,把原先的一个select调用分成了三个部分。

int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, int op, struct epoll_event *event, int maxevent, int timeout);
  1. 调用epoll_create建立了一个epoll对象(file结点),linux内核会创建一个eventpoll结构体,这个结构体拥有两个成员,struct rb_root rbr(红黑树) 和 struct list_head rdllist(双向列表头),rbr用于存储epoll_ctl注册的socket,rdllist用于存储准备就绪的事件。红黑树和双向链表的节点都是使用epitem,里面存储了事件信息。
  2. 调用epoll_ctl,注册监听的事件,把一个socket和这个socket相关的事件添加到这个epoll对象中去,存在红黑树中,当有事件发生时,向准备就绪链表中插入数据。
  3. 调用epoll_wait,等待事件的发生,说白了就是遍历这个双向列表,把这个双向列表的节点数据拷贝出去,拷贝完的就从双向列表里移除。

select和epoll的区别:

情景:当需要读取两个以上的IO时,如果使用阻塞的IO,那么长时间的阻塞在一个描述符上面,另外的描述符虽然有数据但是不能读取出来,这样实时性不能满足高可用的要求,具体可以怎么做呢?

  1. 使用多线程,每个线程读取一个描述符的数据,但是进程或者线程的创建和维护也需要很多的资源开销
  2. 用一个进程,但是使用非阻塞的方式读取数据,当一个IO不可读的时候马上返回,检查下一个是否可读,这种形式的循环为轮询(polling),这种方式比较浪费CPU,因为大多数时间是不可读的,但是任然消耗时间去反复的执行read操作。
  3. 异步IO,当一个描述符准备好的时候,用一个信号告诉进程,但是由于信号量有限,多个描述符时不适用。
  4. 一种比较好的方式就是多路复用,先构造一张有描述符的列表(epoll为队列),然后调用一个函数,直到这些描述符中有准备好的链接时才返回,返回时告诉进程哪些IO准备就绪,select和epoll这两个都是IO多路复用的解决方案。
区别:
  1. select的句柄数量受限,32位的操作系统最多同时监听1024个fd,64位的可以监听2048个fd;而epoll没有,它的限制是最大的打开文件的句柄数量。
  2. epoll的最大好处是不会随着fd数量的增长而降低效率;在select中采用轮询,数据结构采用类似数组的数据结构;epoll是维护一个队列,epoll只会对“活跃”的fd进行操作 – 这是因为内核实现中epoll是根据每个fd上面的callback函数实现的。
  3. epoll使用mmap加速内核与用户态空间的消息传递,无论是select还是epoll都需要把内核的fd消息通知给用户态空间,如何避免不必要的内存拷贝就很重要,epoll是通过内核于用户空间mmap同一块内存实现的。

epoll的两种触发模式

  • LT(水平触发):只要这个文件描述符还有数据可读,每次epoll_wait都会返回它的事件,提醒用户进程去操作。
  • ET(边缘触发):当它检测到有IO事件,通过epoll_wait调用得到事件通知的描述符,对于每一个被通知到的文件描述符,如果可读,则必须将该文件描述符读到空为止,否则下次的epoll_wait不会反回余下的数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值