Open Draymonders opened 4 years ago
正常情况我们会用一个线程去处理一个请求,但是在访问量高的情况下,上下文切换
代价较高。
能不能用单线程就处理很多的请求呢,答案是可以的。
问题是单线程请求数据会丢失么?(即一个请求正在处理,第二个请求发过来)答案是不会的。因为请求一开始并不到达CPU,而是通过硬件->内存->CPU, 所以请求数据可以保存在内存。因而不会丢失。等处理完一个请求后,可以接着处理第二个请求。
bitmap
,对于每个请求,我们都有一个文件句柄fd
, (linux默认连接数是1024,可以修改),因此,我们可以用一个bitmap
的每一位来管理fd
的状态.server
需要去处理请求的时候,我们先在用户态维护一个请求集合的fd_set
, 并且可以分为以下三种状态, read
, write
, exception
的set,分别对应 读请求,写请求,异常请求。fd_set
拷贝到内核态, 如果请求过来,内核态会返回新的fd_set
的状态,然后我们可以o(n)
的遍历fd_set
, 来依次判断 状态是否改变,如果改变,说明传来了新的数据,我们就可以去处理了。o(n)
时间遍历,然后找到有新数据的请求bitmap
长度受控fd_set
不可复用poll采用了如下的pollfd
的结构体,放弃了bitmap
struct pollfd {
int fd;
short events;
short revents;
};
这里处理仍和select
相同,fd
存请求的句柄fd
,events
存想监听的事件IN
OUT
, revents
是反馈。
具体就是 内核态拷贝pollfds
数组,然后监听请求,发现有新的数据到来,便修改revents
,然后返回给用户态
不需要轮询,时间复杂度为O(K)(K为同时来的请求数) (底层使用红黑树) epoll_create 创建一个白板 存放fd_events epoll_ctl 用于向内核注册新的描述符或者是改变某个文件描述符的状态。已注册的描述符在内核中会被维护在一棵红黑树上 epoll_wait 通过回调函数内核会将 I/O 准备好的描述符加入到一个链表中管理,进程调用 epoll_wait() 便可以得到事件完成的描述符
当 epoll_wait() 检测到描述符事件到达时,将此事件通知进程,进程可以不立即处理该事件,下次调用 epoll_wait() 会再次通知进程。是默认的一种模式,并且同时支持 Blocking 和 No-Blocking。
和 LT 模式不同的是,通知之后进程必须立即处理事件。 下次再调用 epoll_wait() 时不会再得到事件到达的通知。很大程度上减少了 epoll 事件被重复触发的次数, 因此效率要比 LT 模式高。只支持 No-Blocking,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。
select 和 poll 均为阻塞(即有请求了,才会返回值) epoll为非阻塞
所述三个均是处理网络请求的
https://www.bilibili.com/video/BV1qJ411w7du