多路复用模型之epoll
2017-11-24 23:07
302 查看
作为多路复用IO模型,epoll致力于解决select与poll设计缺陷以提升系统并发能力。
(1) 并发效率不随文句柄数上升而线性下降:
epoll避免了select模型中对所有每次所有文件描述符句柄轮询。它的底层采用红黑树记录所有文件句柄,并将活跃的连接存放至链表中,在处理IO事件时,只需遍历该链表即可。
(2) mmap加速内核与用户态拷贝:
无论select,poll还是epoll都需要内核把FD消息通知给用户空间,如何避免不必要的内存拷贝就很重要,在linux2.5版本之后,epoll运用mmap内核映射减少了一次内核态至用户态文件描述符拷贝。
(3)操作系统句柄限制:
select受限于进程打开文件描述符限制,虽然通过修改相应的宏并重新编译内核可以解决,但代价太大;epoll在这方面没有明确限制,在1GB内存的机器上大约是10万左右,这个数目和系统内存关系很大。
相应接口:
int epoll_create(int size); //创建epoll句柄,size并不限制句柄数目
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);//事件注册函数
op参数:
a)EPOLL_CTL_ADD, 往事件表中注册fd上的事件;
b)EPOLL_CTL_MOD, 修改fd上注册的事件;
c)EPOLL_CTL_DEL, 删除fd上注册的事件。
struct epoll_event
{
int32_t events; //epoll事件
epoll_data_t data; //用户数据
};
typedef union epoll_data
{
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
}epoll_data;
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout); //等待文件描述符就绪
代码示例:
客户端代码:
运行效果图:
(1) 并发效率不随文句柄数上升而线性下降:
epoll避免了select模型中对所有每次所有文件描述符句柄轮询。它的底层采用红黑树记录所有文件句柄,并将活跃的连接存放至链表中,在处理IO事件时,只需遍历该链表即可。
(2) mmap加速内核与用户态拷贝:
无论select,poll还是epoll都需要内核把FD消息通知给用户空间,如何避免不必要的内存拷贝就很重要,在linux2.5版本之后,epoll运用mmap内核映射减少了一次内核态至用户态文件描述符拷贝。
(3)操作系统句柄限制:
select受限于进程打开文件描述符限制,虽然通过修改相应的宏并重新编译内核可以解决,但代价太大;epoll在这方面没有明确限制,在1GB内存的机器上大约是10万左右,这个数目和系统内存关系很大。
相应接口:
int epoll_create(int size); //创建epoll句柄,size并不限制句柄数目
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);//事件注册函数
op参数:
a)EPOLL_CTL_ADD, 往事件表中注册fd上的事件;
b)EPOLL_CTL_MOD, 修改fd上注册的事件;
c)EPOLL_CTL_DEL, 删除fd上注册的事件。
struct epoll_event
{
int32_t events; //epoll事件
epoll_data_t data; //用户数据
};
typedef union epoll_data
{
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
}epoll_data;
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout); //等待文件描述符就绪
代码示例:
#include <stdio.h> #include <sys/socket.h> #include <sys/un.h> #include <sys/epoll.h> #include <unistd.h> #include <netinet/in.h> #include <arpa/inet.h> #include <fcntl.h> #define BACKLOG 1024 #define MAX_EVENT 1024 #define EPOLL_SIZE 16 #define PORT 9000 int epfd = -1; int main(){ int server_socket = -1; struct sockaddr_in socket_addr; memset(&socket_addr, 0, sizeof(socket_addr)); socket_addr.sin_addr.s_addr = htonl(INADDR_ANY); socket_addr.sin_port = htons(PORT); socket_addr.sin_family = AF_INET; if((server_socket = create_socket(socket_addr, BACKLOG)) < 0) return -1; struct epoll_event event, ev[MAX_EVENT]; memset(&event, 0, sizeof(event)); event.events = EPOLLIN; event.data.fd = server_socket; if((epfd = epoll_init(EPOLL_SIZE, EPOLL_CTL_ADD, server_socket, event)) < 0){ perror("epoll_init error"); return -1; } while(1){ int active_fd_count = epoll_wait(epfd, &ev, MAX_EVENT, -1); int event_index = 0; for(; (event_index < active_fd_count) && (ev[event_index].data.fd) >= 0; event_index++) if(ev[event_index].data.fd == server_socket) connect_handler(server_socket); //处理客户端连接 else if(ev[event_index].events&EPOLLIN) request_handler(ev[event_index].data.fd); //IO读写 else error_handler(ev + event_index); //异常处理 } close(epfd); return 0; } void error_handler(struct epoll_event *event_fd){ close(event_fd->data.fd); struct epoll_event event; memset(&event, 0, sizeof(event)); event.data.fd = event_fd->data.fd; event.events = EPOLLIN | EPOLLERR | EPOLLHUP; epoll_ctl(epfd, EPOLL_CTL_DEL, event_fd->data.fd, &event); event_fd->data.fd = -1; } void request_handler(int client_fd){ if(client_fd < 0){ perror("request_handler client error"); return; } char buf[64]; memset(buf, 0, sizeof(buf)); int len = -1; if((len = read(client_fd, buf, sizeof(buf))) < 0){ perror("read error"); return; }else if(len == 0){ close(client_fd); printf("client close\n"); return; }else{ printf("%s%s", get_client_info(client_fd), buf); write(client_fd, buf, sizeof(buf)); } } void connect_handler(int server_socket){ if(server_socket < 0){ perror("connect error"); return; } int client_fd; struct sockaddr_in client_addr; socklen_t len = sizeof(client_addr); memset(&client_addr, 0, len); if((client_fd = accept(server_socket, &client_addr,&len)) < 0){ perror("accept client error"); return; } printf("[%s:%d] connected\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); set_nonblock(client_fd); struct epoll_event event; memset(&event, 0, sizeof(event)); event.data.fd = client_fd; event.events = EPOLLIN; if(epoll_ctl(epfd, EPOLL_CTL_ADD, client_fd, &event) < 0) perror("epoll_ctl error"); } int epoll_init(int epoll_size, int op, int fd, struct epoll_event event){ if((epfd = epoll_create(epoll_size)) < 0){ perror("epoll create error"); return -1; } if(epoll_ctl(epfd, op, fd, &event) < 0){ perror("epoll ctl error"); return -1; } return epfd; } int create_socket(struct sockaddr_in socket_addr, int backlog){ int server_socket = -1; if((server_socket = socket(AF_INET, SOCK_STREAM, 0)) < 0){ perror("socket error"); return -1; } int on = 1; setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); if(bind(server_socket, &socket_addr, sizeof(socket_addr)) < 0){ perror("bind error"); return -1; } if(listen(server_socket, backlog) < 0){ perror("listen error"); return -1; } return server_socket; } void set_nonblock(int fd){ int ret = -1; if((ret = fcntl(fd, F_GETFL)) < 0){ perror("fcntl get error"); return; } ret = ret | O_NONBLOCK; if(fcntl(fd, F_SETFL,ret) < 0) perror("fcntl set error"); } char *get_client_info(int client_fd){ if(client_fd <= 0) return NULL; char client_info[16]; memset(client_info, 0, 16); struct sockaddr_in peeraddr; socklen_t len = sizeof(peeraddr); memset(&peeraddr, 0, len); getpeername(client_fd, &peeraddr, &len); sprintf(client_info, "[%s:%d]", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port)); return client_info; }
客户端代码:
#include <stdio.h> #include <sys/socket.h> #include <sys/un.h> #include <unistd.h> #include <arpa/inet.h> #define PORT 8080 int main(){ struct sockaddr_in client_addr; memset(&client_addr, 0, sizeof(client_addr)); client_addr.sin_family = AF_INET; client_addr.sin_addr.s_addr = htonl(INADDR_ANY); client_addr.sin_port = htons(PORT); int socket_fd = -1; if((socket_fd = socket(AF_INET, SOCK_STREAM, 0))<0){ perror("socket error"); return -1; } connect(socket_fd, &client_addr, sizeof(client_addr)); char send_buf[64],recv_buf[64]; memset(send_buf, 0, sizeof(send_buf)); memset(recv_buf, 0, sizeof(recv_buf)); while(fgets(send_buf,sizeof(send_buf),stdin)!=NULL){ write(socket_fd, send_buf, strlen(send_buf)); read(socket_fd, recv_buf, sizeof(recv_buf)); fputs(recv_buf, stdout); memset(send_buf, 0, sizeof(send_buf)); memset(recv_buf, 0, sizeof(recv_buf)); } close(socket_fd); return 0; }
运行效果图:
相关文章推荐
- 从零开始写select和epoll I/O多路复用网络模型
- 多路IO复用模型 select epoll 等
- Linux网络通信编程(套接字模型TCP\UDP与IO多路复用模型select\poll\epoll)
- 高并发多路I/O复用之epoll模型
- 多路IO复用模型 select epoll 等
- Linux网络通信编程(套接字模型TCP\UDP与IO多路复用模型select\poll\epoll)
- 多路IO复用模型 select epoll
- IO模型及多路复用IO(select、poll、epoll)
- C/S通信---服务器IO多路复用模型之epoll的使用
- 多路复用I/O模型epoll() 模型 代码实现
- Unix系统编程(8) - I/O多路复用之epoll(Linux)
- Linux下的socket编程实践(七) I/O多路复用技术之select模型
- Linux--高级I/O多路复用之epoll
- Linux I/O多路复用之select,poll与epoll区别
- IO 多路复用之 select、poll、epoll 详解
- Linux开发--IO多路复用之epoll总结
- IO五种模型(阻塞IO、非阻塞IO、多路复用IO、信号驱动IO、异步IO)
- 高并发服务器设计之多路复用模型
- 多路复用之epoll
- Linux IO多路复用之epoll网络编程(含源码)