多路复用之epoll()

自从epoll被引进以来,epoll已经成为了目前实现高性能网络服务器的必备技术,在大数据,高并发,集群等一些名词唱得火热之年代,select和poll的用武之地越来越有限,风头已经被epoll占尽。
相比select模型,poll使用链表保存文件描述符,因此没有了监视文件数量的限制。拿select模型为例,假设我们的服务器需要支持100万的并发连接,则在_FD_SETSIZE为1024的情况下,则我们至少要开辟1k个进程才能实现100万的并发连接。除了进程上下文切换的时间消耗外,从内核/用户空间的无脑内存拷贝,数组轮询等,是系统难以承受的。因此,基于select模型的服务器,很难完成10万级别的并发访问。
epoll是linux内核为处理大批量文件描述符而作了改进的poll,是linux下多路复用select/poll的增强版本,它能显著的提高程序在大量的并发连接中只有少量活跃的情况下的系统CPU利用率。另一个原因就是获取事件的时候,它无需遍历整个被监听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入ready队列的描述符集合就行了。epoll除了提供select/poll那种水平触发,还提供了边缘触发,这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序的效率。
**LT(水平触发):**是缺省的工作方式,并且同时支持block和no-block socket。在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不进行任何操作,内核还是会继续通知你,所以,这种模式编程出错误可能要小一点。传统的的select/poll都是这种模型的代表。
**ET(边缘触发):**是高速的工作方式。只支持non-block socket。在这种模式下,当描述符从未就绪变成就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了,内核不会发送更多的通知,不过在TCP协议中,ET模式的加速效用仍需要更多的benchmark确定。
LT和ET的区别就在这里体现,LT事件不会丢弃触发,而是只要读buff里面有数据可以让用户读,则不断的通知你。而ET则只是在事件发生之时通知。LT模式只要有事件未处理就会触发,而ET则为边缘触发。LT模式只要有事件未处理就要触发,而ET则只在高低电平变换时(状态从1到0或者0到1)触发。

epoll的设计和实现与select完全不同。epoll通过linux内核中申请一个简易的文件系统,把原先的select/poll调用分成了3个部分:

1调用epoll_creat()简历一个epoll对象(在epoll文件系统中为这个句柄对象分配资源)
2调用epoll_ctl向epoll对象中添加这100万个连接的套接字
3调用epoll_wait收集发生的时间

创建epoll epoll_create()

#include <sys/epoll.h>
int epoll_create(int size);

系统调用epoll_creat()创建了一个新的epoll实例,其对应的兴趣列表初始化为空,若成功返回文件描述符,若出错返回-1.参数size指定了我们想要通过epoll实例来检查文件的描述符个数。该参数并不是一个上限,而是告诉内核如何为内部数据结构划分初始大小。
修改epoll的兴趣列表: epoll_ctl()

在这里插入代码片#include <sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *ev);

系统调用epoll_ctl()能够修改由文件描述符epfd所代表的epoll实例中的兴趣列表。若成功返回0,若出错返回-1。
第一个参数epfd 是epoll_create()的返回值;
第二个参数op用来指定需要执行的操作,它可以是如下几种值:
EPOLL_CTL_ADD:将描述符fd添加到epoll实例中的兴趣列表中去。对于fd上我们感兴趣的事件,都指定在ev所指向的结构体中。如果我们试图向兴趣列表中添加一个已存在的文件描述符,epoll_ctl()将出现EEXIST错误

EPOLL_CTL_MOD:修改描述符上设定的事件,需要用到由ev所指向的结构体中的信息。如果我们试图修改不在兴趣列表中的文件描述符,epoll_ctl()将出现ENOENT错误;

EPOLL_CTL_DEL:将文件描述符fd从epfd的兴趣列表中移除,该操作忽略参数ev。如果我们试图移除一个不在epfd的兴趣列表中的文件描述符,epoll_ctl()将出现ENOENT错误。关闭一个文件描述符会自动将其从所有的epoll实例的兴趣列表移除;
第三个参数fd指明了要修改兴趣列表中的哪一个文件描述符的设定。该参数可以是代表管道、FIFO、套接字、POSIX消息队
列、inotify实例、终端、设备,甚至是另一个epoll实例的文件描述符。但是,这里fd不能作为普通文件或目录的文件描述符;
第四个参数ev是指向结构体epoll_event的指针,结构体的定义如下:

typedef union epoll_data
{
   
 void *ptr; /* Pointer to user-defind data */
 int fd; /* File descriptor */
 uint32_t u32; /* 32-bit integer */
 uint64_t u64; /* 64-bit integer */
} epoll_data_t;
struct epoll_event
{
   
 uint32_t events; /* epoll events(bit mask) */
 epoll_data_t data; /* User data */
};

参数ev为文件描述符fd所做的设置(epoll_event)如下:

events字段是一个位掩码,它指定了我们为待检查的描述符fd上所感兴趣的事件集合;
data字段是一个联合体,当描述符fd稍后称为就绪态时,联合的成员可用来指定传回给调用进程的信息;
事件等待: epoll_wait()

#include <sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event *evlist, int maxevents, int timeout);

系统调用epoll_wait()返回epoll实例中处于就绪态的文件描述符信息,单个epoll_wait()调用能够返回多个就绪态文件描述符的信息。调用成功后epoll_wait()返回数组evlist中的元素个数,如果在timeout超时间隔内没有任何文件描述符处于就绪态的话就返回0,出错时返回-1并在errno中设定错误码以表示错误原因。
第一个参数epfd是epoll_create()的返回值;
第二个参数evlist所指向的结构体数组中返回的是有关就绪态文件描述符的信息,数组evlist的空间由调用者负责申请;
第三个参数maxevents指定所evlist数组里包含的元素个数;
第四个参数timeout用来确定epoll_wait()的阻塞行为,有如下几种:
如果timeout等于-1,调用将一直阻塞,直到兴趣列表中的文件描述符上有事件产生或者直到捕获到一个信号为止。
如果timeout等于0,执行一次非阻塞式地检查,看兴趣列表中的描述符上产生了哪个事件。
如果timeout大于0,调用将阻塞至多timeout毫秒,直到文件描述符上有事件发生,或者直到捕获到一个信号为止。
下面是使用epoll()多路复用实现的服务器端示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#include <pthread.h>
#include <getopt.h>
#include <libgen.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <sys/resource.h>
#define MAX_EVENTS 512
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
static inline void print_usage(char *progname);
int socket_server_init(char *listen_ip, int listen_port);
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值