无锡网站建设推广公司,网站建设总结体会,2023 个人主页html源码,天津网站建设平台epoll 是一种在 Linux 系统上用于高效事件驱动编程的 I/O 多路复用机制。它相比于传统的 select 和 poll 函数具有更好的性能和扩展性。
epoll 的主要特点和原理#xff1a;
1、事件驱动#xff1a;epoll 是基于事件驱动的模型#xff0c;它通过监听事件来触发相应的回调函… epoll 是一种在 Linux 系统上用于高效事件驱动编程的 I/O 多路复用机制。它相比于传统的 select 和 poll 函数具有更好的性能和扩展性。
epoll 的主要特点和原理
1、事件驱动epoll 是基于事件驱动的模型它通过监听事件来触发相应的回调函数而不是像传统的阻塞模型那样持续轮询。这样可以避免无效的轮询操作提高效率。
2、高效epoll 使用了红黑树rbtree和哈希表hash table的数据结构来存储和管理大量的文件描述符使得在大规模连接的情况下对文件描述符的管理和查找操作具备较高的效率。
3、边缘触发epoll 提供了边缘触发edge-triggered的工作模式。在边缘触发模式下只有当文件描述符的状态发生变化时epoll 才会通知应用程序。这使得应用程序能够更精确地处理事件避免了事件的丢失和重复触发。
4、扩展性epoll 支持高并发的连接可以同时监听大量的文件描述符且不随文件描述符数量的增加而性能下降。它采用了事件通知的方式只有在文件描述符发生状态变化时才会通知应用程序避免了大量的轮询操作。
使用 epoll 的基本步骤如下
1、创建 epoll 实例通过调用 epoll_create 函数创建一个 epoll 对象。
2、将需要监听的文件描述符加入 epoll 实例通过调用 epoll_ctl 函数将文件描述符添加到 epoll 中并指定需要监听的事件类型。 epoll_ctl 是一个用于控制 epoll 实例的系统调用函数它用于向 epoll 实例中添加、修改或删除文件描述符及其关联的事件。
3、 进入事件循环调用 epoll_wait 函数等待事件发生。该函数会阻塞程序执行直到有事件发生或超时。 epoll_wait 是一个用于等待事件的系统调用函数它在 epoll 实例上进行阻塞等待直到有事件就绪或超时。
4、 当 epoll_wait 返回时根据返回的就绪事件进行相应的处理。可以通过遍历返回的事件列表来获取就绪的文件描述符和事件类型。 epoll 在网络编程中广泛应用特别适用于高并发的服务器开发能够处理大量的并发连接和高频率的 I/O 事件。它提供了高效的事件驱动模型可以大大提升程序的性能和可扩展性。
一、epoll_create
int epoll_create(int size);epoll_create 函数接受一个参数 size该参数指定了 epoll 实例所能处理的最大文件描述符数目。它返回一个整数值表示 epoll 实例的文件描述符用于后续的 epoll 相关操作。
入参 size 参数是一个提示值用于告诉内核 epoll 实例需要处理的最大文件描述符数目。内核会根据此提示值进行一些优化但实际上该值在大多数情况下并不会限制 epoll 实例所能处理的文件描述符数目。
返回值
成功时返回一个非负整数表示 epoll 实例的文件描述符。如果调用失败返回值为 -1并设置相应的错误码可以通过 errno 来获取具体的错误信息。
epoll_create 函数创建的 epoll 实例是默认的边缘触发模式Edge Triggered Mode。这意味着当文件描述符上的事件状态从未就绪变为就绪时epoll 会返回该事件而不是只在事件状态为就绪时返回一次。
在使用完 epoll 实例后应当使用 close 函数显式关闭 epoll 实例的文件描述符以释放相关资源。
二、epoll_ctl
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);入参
epfd表示 epoll 实例的文件描述符即通过 epoll_create 创建的返回值。op表示要进行的操作类型可以是以下三种值之一 EPOLL_CTL_ADD将文件描述符 fd 添加到 epoll 实例中关联的事件由 event 参数指定。EPOLL_CTL_MOD修改已添加到 epoll 实例中的文件描述符 fd 的关联事件新的事件由 event 参数指定。EPOLL_CTL_DEL从 epoll 实例中删除文件描述符 fd。 fd表示要添加、修改或删除的文件描述符。event指向一个 struct epoll_event 结构体的指针用于设置文件描述符的关联事件。 event 字段表示要关注的事件类型可以是以下事件类型的组合 EPOLLIN表示可读事件。EPOLLOUT表示可写事件。EPOLLRDHUP表示对端关闭连接或关闭写端。EPOLLPRI表示有紧急数据可读。EPOLLERR表示发生错误。EPOLLHUP表示连接关闭。EPOLLET使用边缘触发模式Edge Triggered Mode。EPOLLONESHOT在事件触发后将文件描述符从 epoll 实例中删除需要重新添加才能再次触发。
struct epoll_event {__uint32_t events; // 表示要关注的事件类型epoll_data_t data; // 用户数据可以是文件描述符或指针
};typedef union epoll_data {void *ptr; // 指针类型的用户数据int fd; // 文件描述符类型的用户数据__uint32_t u32;__uint64_t u64;
} epoll_data_t;返回值
成功时返回 0失败时返回 -1并设置相应的错误码可以通过 errno 来获取具体的错误信息。
三、epoll_wait
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);入参
epfd表示 epoll 实例的文件描述符即通过 epoll_create 创建的返回值。events指向一个 struct epoll_event 数组的指针用于存储就绪的事件信息。maxevents表示 events 数组的大小即最多可以存储的事件数目。timeout表示等待的超时时间以毫秒为单位。可以是以下值之一 -1表示永久阻塞直到有事件就绪。0表示非阻塞立即返回。大于 0表示超时时间等待指定的毫秒数后返回。
返回值
epoll_wait 函数用于阻塞等待 epoll 实例上的事件就绪。当有事件就绪时它将填充 events 数组并返回就绪事件的数量。如果 epoll_wait 函数返回值大于 0则表示有就绪事件并且可以通过遍历 events 数组来获取每个就绪事件的相关信息。如果 epoll_wait 函数返回值为 0表示超时时间到达即没有事件就绪。如果 epoll_wait 函数返回值为 -1表示调用出错可以通过 errno 来获取具体的错误信息。
四、代码实现
#include iostream
//socket
#include sys/types.h
#include sys/socket.h
//close
#include unistd.h
//exit
#include stdlib.h
//perror
#include stdio.h
//memset
#include string.h
//htons
#include arpa/inet.h
/* According to earlier standards */
#include sys/time.h
//epoll
#include sys/epoll.h#define PORT 8596
#define MESSAGE_SIZE 1024
#define FD_SIZE 1024
#define MAX_EVENTS 20
#define TIME_OUT 500int main(){int ret-1;int socket_fd-1;int accept_fd-1;int backlog10;int flags1;struct sockaddr_in local_addr,remote_addr;struct epoll_event ev,events[FD_SIZE];int epoll_fd-1;int event_number0;//create socketsocket_fdsocket(AF_INET,SOCK_STREAM,0);if(socket_fd -1){perror(create socket error);exit(1);}//set option of socketret setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, flags, sizeof(flags));if ( ret -1 ){perror(setsockopt error);}//set socket addresslocal_addr.sin_familyAF_INET;local_addr.sin_porthtons(PORT);local_addr.sin_addr.s_addrINADDR_ANY;bzero((local_addr.sin_zero),8);//bind socketretbind(socket_fd, (struct sockaddr *)local_addr,sizeof(struct sockaddr_in));if(ret -1){perror(bind socket error);exit(1);}retlisten(socket_fd, backlog);if(ret -1){perror(listen error);exit(1);}//创建epollepoll_fdepoll_create(256);ev.data.fdsocket_fd;ev.eventsEPOLLIN;//将socket_fd加入到epoll中epoll_ctl(epoll_fd,EPOLL_CTL_ADD,socket_fd,ev);//loop to accept clientfor(;;){event_numberepoll_wait(epoll_fd,events,MAX_EVENTS,TIME_OUT);for(int i0;ievent_number;i){if(events[i].data.fdsocket_fd){socklen_t addrlen sizeof(remote_addr);accept_fdaccept(socket_fd,( struct sockaddr *)remote_addr, addrlen);ev.data.fdaccept_fd;ev.eventsEPOLLIN | EPOLLET;//添加accept_fd到epoll中//添加accept_fd到epoll中if((epoll_ctl(epoll_fd,EPOLL_CTL_ADD,accept_fd,ev))-1){close(accept_fd);} }else if(events[i].events EPOLLIN){//有数据可读char in_buf[MESSAGE_SIZE];memset(in_buf, 0, MESSAGE_SIZE);//receive dataret recv( events[i].data.fd, in_buf, MESSAGE_SIZE, 0 );if(ret 0){switch (errno){case EAGAIN: //暂时没有数据break;case EINTR: //被终断ret recv(events[i].data.fd, in_buf, MESSAGE_SIZE, 0);break;default:printf(the client is closed, fd:%d\n, events[i].data.fd);epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, ev);close(events[i].data.fd);break;} } printf(receive message:%s\n, in_buf);send(events[i].data.fd, in_buf, ret, 0);}}}printf(quit server....);// 关闭监听 socket 和 epoll 实例close(socket_fd);close(epoll_fd);return 0;
}
服务端 客户端1 客户端2
从代码直观就能看出epoll的代码量和逻辑实现相比select都特别简洁。epoll 在网络编程中广泛应用特别适用于高并发的服务器开发能够处理大量的并发连接和高频率的 I/O 事件。它提供了高效的事件驱动模型可以大大提升程序的性能和可扩展性。