①Apache模子(Process Per Connection,简称PPC) 和 TPC(Thread Per Connection)模子
这两种模子思想类似,就是让每一个到来的连接都有一个进程/线程来服务。这种模子的代价是它要时间和空间。连接较多时,进程/线程切换的开销比力大。因此这类模子能接受的最大连接数都不会高,一般在几百个左右。
epoll只有epoll_create,epoll_ctl,epoll_wait 3个系统调用。
1: #include 2: 3: int epoll_create(int size); 4: 5: int epoll_ctl(int epfd, int op, int fd, structepoll_event *event); 6: 7: int epoll_wait(int epfd, struct epoll_event* events, int maxevents. int timeout); 8: 9: ① int epoll_create(int size);
创建一个epoll的句柄。自从linux2.6.8之后,size参数是被忽略的。需要留意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查察/proc/进程id/fd/,是能够看到这个fd的,以是在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。 ②int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll的事件注册函数,它不同于select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。
第一个参数是epoll_create()的返回值。
第二个参数表现动作,用三个宏来表现:
EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd;
第三个参数是需要监听的fd。
第四个参数是告诉内核需要监听什么事,struct epoll_event结构如下:
1: //保存触发事件的某个文件形貌符相关的数据(与详细使用方式有关) 2: 3: typedef union epoll_data { 4: void *ptr; 5: int fd; 6: __uint32_t u32; 7: __uint64_t u64; 8: } epoll_data_t; 9: //感兴趣的事件和被触发的事件 10: struct epoll_event { 11: __uint32_t events; /* Epoll events */ 12: epoll_data_t data; /* User data variable */ 13: };events可以是以下几个宏的集合:
EPOLLIN :表现对应的文件形貌符可以读(包罗对端SOCKET正常关闭);
EPOLLOUT:表现对应的文件形貌符可以写;
EPOLLPRI:表现对应的文件形貌符有紧急的数据可读(这里应该表现有带外数据到来);
EPOLLERR:表现对应的文件形貌符发生错误;
EPOLLHUP:表现对应的文件形貌符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里
③ int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
收集在epoll监控的事件中已经发送的事件。参数events是分配好的epoll_event结构体数组,epoll将会把发生的事件赋值到events数组中(events不可以是空指针,内核只负责把数据复制到这个events数组中,不会去帮助我们在用户态中分配内存)。maxevents告之内核这个events有多大,这个 maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永世壅闭)。如果函数调用成功,返回对应I/O上已准备好的文件形貌符数量,如返回0表现已超时。 【文章福利】:小编整理了一些个人觉得比力好的学习册本、视频资料共享在群文件内里,有需要的可以自行添加哦!~点击加入832218493(君羊需要自取)
② 此外,内核使用了slab机制,为epoll提供了快速的数据结构:
在内核里,统统皆文件。以是,epoll向内核注册了一个文件系统,用于存储上述的被监控的fd。当你调用epoll_create时,就会在这个假造的epoll文件系统里创建一个file结点。当然这个file不是普通文件,它只服务于epoll。epoll在被内核初始化时(操作系统启动),同时会开发出epoll自己的内核高速cache区,用于安置每一个我们想监控的fd,这些fd会以红黑树的形式保存在内核cache里,以支持快速的查找、插入、删除。这个内核高速cache区,就是建立连续的物理内存页,然后在之上建立slab层,简单的说,就是物理上分配好你想要的size的内存对象,每次使用时都是使用空闲的已分配好的对象。
③ epoll的第三个优势在于:当我们调用epoll_ctl往里塞入百万个fd时,epoll_wait仍然可以飞快的返回,并有效的将发生事件的fd给我们用户。这是由于我们在调用epoll_create时,内核除了帮我们在epoll文件系统里建了个file结点,在内核cache里建了个红黑树用于存储以后epoll_ctl传来的fd外,还会再建立一个list链表,用于存储准备就绪的事件,当epoll_wait调用时,仅仅观察这个list链表里有没有数据即可。有数据就返回,没有数据就sleep,比及timeout时间到后即使链表没数据也返回。以是,epoll_wait非常高效。而且,通常情况下即使我们要监控百万计的fd,大多一次也只返回很少量的准备就绪fd而已,以是,epoll_wait仅需要从内核态copy少量的fd到用户态而已。那么,这个准备就绪list链表是怎么维护的呢?当我们执行epoll_ctl时,除了把fd放到epoll文件系统里file对象对应的红黑树上之外,还会给内核中断处置处罚程序注册一个回调函数,告诉内核,如果这个fd的中断到了,就把它放到准备就绪list链表里。以是,当一个fd(比方socket)上有数据到了,内核在把设备(比方网卡)上的数据copy到内核中后就来把fd(socket)插入到准备就绪list链表里了。
如此,一颗红黑树,一张准备就绪fd链表,少量的内核cache,就帮我们解决了大并发下的fd(socket)处置处罚题目。
1.执行epoll_create时,创建了红黑树和就绪list链表。
2.执行epoll_ctl时,如果增加fd(socket),则检查在红黑树中是否存在,存在立即返回,不存在则添加到红黑树上,然后向内核注册回调函数,用于当中断事件来暂时向准备就绪list链表中插入数据。
3.执行epoll_wait时立刻返回准备就绪链表里的数据即可。
6.Epoll源码分析