IO多路复用,epoll、poll、select的区别
目前服务器主要采取接收请求的方式主要有三种,多线程接收请求,
select, epoll。
理解:
- io多路复用:https://blog.csdn.net/qq_28229449/article/details/85052922,阻塞io和io多路复用的区别是,多路io复用能够阻塞监听多个文件描述符。所以才能多路复用。
- select 首先将文件描述符数组copy到内核态中,等到文件描述符中有事件就绪,内核遍历文件描述符数组到返回就绪文件描述符个数,随后用户态再次进行文件描述符遍历。
- poll和select区别是,poll利用链表存储文件描述符,没有大小限制
- epoll是在内核态维护了文件描述符集合(利用红黑树存储), epoll采用的是事件驱动机制,不需要通过轮询来找到对应的那个文件描述符。当某个文件描述符就绪的时候,会通过回调函数,把它放到一个就绪链表中记录起来,内核就不用遍历文件描述符了。
内核只返回就绪的文件描述符集合,也就是上面提到的那个就绪链表,因为返回给用户态的肯定是已经就绪了的,所以用户态可以拿来即用,也无需做多余的遍历。 - 内核阻塞还是while,这点还是不理解,个人理解select时,有文件描述符就绪唤醒阻塞线程(改变线程状态标志??),线程遍历文件描述符数组,返回就绪文件描述符个数。而epoll文件描述符就绪时,出发回调函数,将文件描述符放置到就绪链表中。内核返回就绪链表给用户态,用户态无需做多余遍历。
Socket常见函数
1.创建serverSocket
2.bing (ip, port)
3. listen(serversocket的文件描述符,全链接队列长度),(设置里全链接队列长度),此时如果有请求来,内核已经帮助完成三次握手。
4. 在执行accept函数前就完成了
5. accept()函数从全连接队列取出一个socket连接,并绑定一个描述符d。(代表着服务端与客户端的连接)。
6. 利用文件描述符d,收发数据。
6.close函数,发生4次挥手。(客户端发送FIN, 进入FIN_WAIT_1状态, 收到ack进入FIN_WAIT_2状态,因为一般server会直接发回ACK, 所以WAIT1状态很难见到, 收到ACK后,客户端进入FIN_WAIT_2状态,server进入ClOSE_WAIT状态,表示还有数据要发送,server发送完数据,发送和FIN报文, 客户端接收到FIN报文后,发送给服务端ACK, 变成TIME_WAIT状态, TIME_WAIT一般存在两个2MSL时间,原因是避免发送给服务端的ACK丢失,继续接收到服务端PIN请求,无法处理。保证Server端连接能够关闭。
Select函数博客地址
https://www.cnblogs.com/Anker/archive/2013/08/14/3258674.html
poll博客地址
https://www.cnblogs.com/Anker/archive/2013/08/15/3261006.html
epoll博客地址
https://www.cnblogs.com/Anker/archive/2013/08/17/3263780.html
区别:https://www.cnblogs.com/sky-heaven/p/7011684.html
select函数:
1.每次select文件描述符集合都发生了变化,要重新复制。
2 .select函数每次调用都要拷贝serverSocket的文件描述符和客户端的文件描述符集合到内核中,内核遍历文件描述符,然后将每个文件描述符的等待队列放入当前进程(注意此时进程并为阻塞)。当文件描述符事件就绪时(内核会控制监听文件描述符的事件),唤醒等待队里当前进程,进程开始遍历文件描述符。
3. 每次内核遍历文件描述符时,还会看文件描述符事件是否就绪,如果就绪重新给文件描述符集合赋值(这就话理解为)可能会改变文件描述符的状态,方便判断是否就绪。
4. 如果没有就绪,则阻塞,等待操作系统唤醒等待进程,重新遍历文件描述符的过程。
5. 理解:遍历文件描述符时,一边会将进程加入等待队列,一边会判断文件描述符事件是否就绪。
poll函数与select的区别是大小没有限制
epoll函数:
epoll函数由三个函数组成,
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
a.int epoll_create返回的是一个存储着(文件描述符集合想对应的监听事件)的句柄,size表示句柄对应集合能够存储文件描述符的大小。
b.int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event), epoll_ctl表示将文件描述符fd和对应事件event,绑定到epfd上。
c.i nt epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout),会返回就绪事件的个数,就绪事件在event中。
epoll每次将进程挂载在设备描述符的等待队列只挂载一次,通过句柄epfd管理事件, 每次事件就绪后唤醒,阻塞进程current,并将就绪文件描述符相关事件,加入到就绪集合中, current去检测是就绪集合每次被唤醒,只是去检测就绪集合是否为空,不为空则返回就绪事件,而select每次被唤醒要遍历一面在内核中的文件描述符,导致性能消耗。
epoll用一个描述符epfd管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。