Linux I/O复用——epoll()

2018-11-18  本文已影响10人  编程小兔崽

编程TWO 编程小兔崽 今天

epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。epoll除了提供select/poll那种IO事件的水平触发外,还提供了边缘触发,这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。

1、epoll()

  epoll()是Linux特有的I/O复用函数,它的实现与使用上和select()、poll()、有很大差异。

  epoll()用一组函数来完成任务,而不是单个函数;其次,epoll()把文件描述放到内核事件表中,只需一个额外的文件描述符,来标识内核中唯一的这个事件表。

需要使用的API:

  int epoll_create(int size);

  int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

需要使用的结构体信息:

typedef union epoll_data

{

void        *ptr;

int          fd;  //一般情况下,都用的是这个文件描述符

uint32_t     u32;

uint64_t     u64;

} epoll_data_t;

struct epoll_event{

uint32_t     events;      /* Epoll events */

epoll_data_t data;        /* User data variable */

};

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

2、epoll_wait()

  关键:对epoll_wait()函数的核心理解

  (1)、返回值:事件表中就绪客户端的个数;

  (2)、参数events:将事件表中的就绪客户端的信息放到了events数组中。

3、epoll()的核心思想

   就是创建一个内核事件表,存放所监听客户端的套接字和当前的事件,在利用epoll_wait()函数查找就绪的套接字,最后经过增加、删除、修改利用epoll_ctl()函数进行;当然了,这其中还有一批搭配使用的宏;

3、代码实现

(1)、utili.h

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define SERVER_IP "127.0.0.1"

#define SERVER_PORT  8787

#define LISTEN_QUEUE 5

#define SIZE 10

#define BUFFER_SIZE 256#include

#define OPEN_MAX 1000

#include

#define FDSIZE      1000

#define EPOLLEVENTS 100

(2)、ser.c

#include"../utili.h"

static int socket_bind(const char *ip, int port)

{

int listenfd = socket(AF_INET, SOCK_STREAM, 0);

struct sockaddr_in addrSer;

addrSer.sin_family = AF_INET;

//addrSer.sin_addr.s_addr = inet_addr(ip);

inet_pton(AF_INET, ip, &addrSer.sin_addr);

addrSer.sin_port = htons(port);

bind(listenfd, (struct sockaddr*)&addrSer, sizeof(struct sockaddr));

return listenfd;

}

static void add_event(int epollfd, int fd, int state){

struct epoll_event ev;

ev.events = state;

ev.data.fd = fd;

epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);

}

static void delete_event(int epollfd, int fd, int state){

struct epoll_event ev;

ev.events = state;

ev.data.fd = fd;

epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &ev);

}

static void modify_event(int epollfd, int fd, int state){

struct epoll_event ev;

ev.events = state;

ev.data.fd = fd;

epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &ev);

}

static void handle_accept(int epollfd, int listenfd)

{

int clifd;

struct sockaddr_in addrCli;

socklen_t len = sizeof(struct sockaddr);

clifd = accept(listenfd, (struct sockaddr*)&addrCli, &len);

if(clifd != -1)

{

add_event(epollfd, clifd, EPOLLIN);

}

}

static void do_read(int epollfd,  int fd, char *buf){

int nread = read(fd, buf, BUFFER_SIZE);

if(nread == -1)

{

close(fd);

delete_event(epollfd, fd, EPOLLIN);

}

else

{

printf("read msg:>%s\n",buf);

modify_event(epollfd, fd, EPOLLOUT);

}

}

static void do_write(int epollfd, int fd, char *buf){

int nwrite = write(fd, buf, strlen(buf)+1);

if(nwrite == -1)

{

close(fd);

delete_event(epollfd, fd, EPOLLOUT);

}

else

{

modify_event(epollfd, fd , EPOLLIN);

}

memset(buf, 0, BUFFER_SIZE);

}

static void handle_events(int epollfd, struct epoll_event *events, int num,

int listenfd, char *buf)

{

int i;

int fd;

for(i=0; i

{

fd = events[i].data.fd;

if((fd==listenfd) && (events[i].events&EPOLLIN)) //根据其结果分别进入三种状态

handle_accept(epollfd, listenfd);  //申请与服务器连接

else if(events[i].events & EPOLLIN)

do_read(epollfd, fd, buf);  //只读

else if(events[i].events & EPOLLOUT)

do_write(epollfd, fd, buf);  //只写

}

}

static void do_epoll(int listenfd)

{

int ret;

char buffer[BUFFER_SIZE];

struct epoll_event events[EPOLLEVENTS];

int epollfd = epoll_create(FDSIZE);

add_event(epollfd, listenfd, EPOLLIN);

for(;;)

{

//select poll

ret = epoll_wait(epollfd, events, EPOLLEVENTS, -1);

handle_events(epollfd, events, ret, listenfd, buffer);

}

close(epollfd);}int main(void)

{

int listenfd;

listenfd = socket_bind(SERVER_IP, SERVER_PORT);

listen(listenfd, LISTEN_QUEUE);

do_epoll(listenfd);

return 0;

}

(3)、cli.c

#include"../utili.h"

static void add_event(int epollfd, int fd, int state)

{

struct epoll_event ev;

ev.events = state;

ev.data.fd = fd;

epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);

}

static void delete_event(int epollfd, int fd, int state){

struct epoll_event ev;

ev.events = state;

ev.data.fd = fd;

epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &ev);}static void modify_event(int epollfd, int fd, int state){

struct epoll_event ev;

ev.events = state;

ev.data.fd = fd;

epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &ev);}static void do_read(int epollfd,  int fd, int sockfd, char *buf){

int nread = read(fd, buf, BUFFER_SIZE);

if(nread == -1)

{

close(fd);

delete_event(epollfd, fd, EPOLLIN);

}

else

{

if(fd == STDIN_FILENO)

add_event(epollfd, fd, EPOLLIN);

else

{

delete_event(epollfd, fd, EPOLLIN);

add_event(epollfd, STDOUT_FILENO, EPOLLOUT);

}

}

printf("Ser :>%s", buf);}static void do_write(int epollfd, int fd, int sockfd, char *buf){

int nwrite = write(fd, buf, strlen(buf)+1);

if(nwrite == -1)

{

perror("write");

close(fd);

}

else

{

if(fd == STDOUT_FILENO)

{

delete_event(epollfd, fd, EPOLLOUT);

}

else

{

modify_event(epollfd, fd, EPOLLIN);

}

}

memset(buf, 0, BUFFER_SIZE);}static void handle_events(int epollfd, struct epoll_event *events, int num,

int sockfd, char *buf)

{

int i;

int fd;

for(i=0; i

{

fd = events[i].data.fd;

if(events[i].events & EPOLLIN)

do_read(epollfd, fd, sockfd, buf);

else if(events[i].events, fd, sockfd, buf)

do_write(epollfd, fd, sockfd, buf);

}

}

static void handle_connection(int sockfd)

{

struct epoll_event events[EPOLLEVENTS];

int epollfd = epoll_create(FDSIZE);

add_event(epollfd, STDIN_FILENO, EPOLLIN);

int ret;

char buffer[BUFFER_SIZE];

for(;;)

{

ret = epoll_wait(epollfd, events, EPOLLEVENTS, -1);

handle_events(epollfd, events, ret, sockfd, buffer);

}

close(epollfd);}int main(void)

{

int sockfd = socket(AF_INET, SOCK_STREAM, 0);

struct sockaddr_in addrSer;

addrSer.sin_family = AF_INET;

addrSer.sin_port = htons(SERVER_PORT);

addrSer.sin_addr.s_addr = inet_addr(SERVER_IP);

connect(sockfd, (struct sockaddr*)&addrSer, sizeof(struct sockaddr));

handle_connection(sockfd);

close(sockfd);

return 0;

}

运行结果

服务器端就是等待客户端的使用;

客户端1

客户端2

利用epoll()函数,不用轮询每个套接字,效率更高效一些;

Linux编程重点的知识也跟大家讲了一部分,接下来会跟大家分享进程之间的通信、线程之间的通信这些也是平时面试和开发经常用到的知识,还有一个跟大家说一下,最近工作比较忙,项目比较紧,可能更新文章没有那么频繁,不过基本上每周都会更新至少3篇,谢谢大家的支持,加油每天进步一点点!

推荐阅读:

Linux I/O复用——poll()

Linux I/O复用—select()

线程池网络服务

多线程网络服务

Socket网络编程

线程高级操作

Linux多线程编程

线程

欢本文的朋友们,欢迎长按下图关注订阅号编程小兔崽,收看更多精彩内容

每天进步一点点,如果有用给小编点个赞

上一篇下一篇

猜你喜欢

热点阅读