linux epoll的使用

  • Post author:
  • Post category:linux


epoll的使用:

#include <sys/epoll.h>
int epoll_create(int size);
int epoll_create1(int flags);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

epoll与select、poll的区别:

相比于select与poll,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。
内核中的select与poll的实现是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。
epoll的实现是基于回调的,如果fd有期望的事件发生就通过回调函数将其加入epoll就绪队列中,也就是说它只关心“活跃”的fd,与fd数目无关。
内核 / 用户空间 内存拷贝问题,如何让内核把 fd消息通知给用户空间呢?在这个问题上select/poll采取了内存拷贝方法。而epoll采用了共享内存的方式。
epoll不仅会告诉应用程序有I/0 事件到来,还会告诉应用程序相关的信息,这些信息是应用程序填充的,因此根据这些信息应用程序就能直接定位到事件,而不必遍历整个fd集合

EPOLLLT:

完全靠kernel epoll驱动,应用程序只需要处理从epoll_wait返回的fds,这些fds我们认为它们处于就绪状态。

EPOLLET:

此模式下,系统仅仅通知应用程序哪些fds变成了就绪状态,一旦fd变成就绪状态,epoll将不再关注这个fd的任何状态信息,(从epoll队列移除)直到应用程序通过读写操作触发EAGAIN状态,epoll认为这个fd又变为空闲状态,那么epoll又重新关注这个fd的状态变化(重新加入epoll队列)。
随着epoll_wait的返回,队列中的fds是在减少的,所以在大并发的系统中,EPOLLET更有优势。但是对程序员的要求也更高。

epollsrv.c

#include <unistd.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <signal.h>

#include <fcntl.h>

#include <sys/wait.h>

#include <sys/epoll.h>

#include <stdlib.h>

#include <stdio.h>

#include <errno.h>

#include <string.h>

#include <vector>

#include <algorithm>

typedef std::vector<struct epoll_event> EventList;//定义一个vector,里面存有epoll_event结构

#define ERR_EXIT(m) \

do \

{ \

perror(m); \

exit(EXIT_FAILURE); \

} while(0)

void activate_nonblock(int fd)

{

int ret;

int flags = fcntl(fd, F_GETFL);

if (flags == -1)

ERR_EXIT(“fcntl”);

flags |= O_NONBLOCK;

ret = fcntl(fd, F_SETFL, flags);

if (ret == -1)

ERR_EXIT(“fcntl”);

}

ssize_t readn(int fd, void *buf, size_t count)

{

size_t nleft = count;

ssize_t nread;

char *bufp = (char*)buf;

while (nleft > 0)

{

if ((nread = read(fd, bufp, nleft)) < 0)

{

if (errno == EINTR)

continue;

return -1;

}

else if (nread == 0)

return count – nleft;

bufp += nread;

nleft -= nread;

}

return count;

}

ssize_t writen(int fd, const void *buf, size_t count)

{

size_t nleft = count;

ssize_t nwritten;

char *bufp = (char*)buf;

while (nleft > 0)

{

if ((nwritten = write(fd, bufp, nleft)) < 0)

{

if (errno == EINTR)

continue;

return -1;

}

else if (nwritten == 0)

continue;

bufp += nwritten;

nleft -= nwritten;

}

return count;

}

ssize_t recv_peek(int sockfd, void *buf, size_t len)

{

while (1)

{

int ret = recv(sockfd, buf, len, MSG_PEEK);

if (ret == -1 && errno == EINTR)

continue;

return ret;

}

}

ssize_t readline(int sockfd, void *buf, size_t maxline)

{

int ret;

int nread;

char *bufp = (char*)buf;

int nleft = maxline;

while (1)

{

ret = recv_peek(sockfd, bufp, nleft);

if (ret < 0)

return ret;

else if (ret == 0)

return ret;

nread = ret;

int i;

for (i=0; i<nread; i++)

{

if (bufp[i] == ‘\n’)

{

ret = readn(sockfd, bufp, i+1);

if (ret != i+1)

exit(EXIT_FAILURE);

return ret;

}

}

if (nread > nleft)

exit(EXIT_FAILURE);

nleft -= nread;

ret = readn(sockfd, bufp, nread);

if (ret != nread)

exit(EXIT_FAILURE);

bufp += nread;

}

return -1;

}

void handle_sigchld(int sig)

{

/*    wait(NULL);*/

while (waitpid(-1, NULL, WNOHANG) > 0)

;

}

void handle_sigpipe(int sig)

{

printf(“recv a sig=%d\n”, sig);

}

int main(void)

{

int count = 0;

signal(SIGPIPE, handle_sigpipe);

signal(SIGCHLD, handle_sigchld);

int listenfd;

if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)

ERR_EXIT(“socket”);

struct sockaddr_in servaddr;

memset(&servaddr, 0, sizeof(servaddr));

servaddr.sin_family = AF_INET;

servaddr.sin_port = htons(5188);

servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

int on = 1;

if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)

ERR_EXIT(“setsockopt”);

if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)

ERR_EXIT(“bind”);

if (listen(listenfd, SOMAXCONN) < 0)

ERR_EXIT(“listen”);

std::vector<int> clients;

int epollfd;

epollfd = epoll_create1(EPOLL_CLOEXEC);//创建一个epoll实例

struct epoll_event event;

event.data.fd = listenfd;

event.events = EPOLLIN | EPOLLET;

epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &event);

EventList events(16);

struct sockaddr_in peeraddr;

socklen_t peerlen;

int conn;

int i;

int nready;

while (1)

{

nready = epoll_wait(epollfd, &*events.begin(), static_cast<int>(events.size()), -1);

if (nready == -1)

{

if (errno == EINTR)

continue;

ERR_EXIT(“epoll_wait”);

}

if (nready == 0)

continue;

if ((size_t)nready == events.size())

events.resize(events.size()*2);

for (i = 0; i < nready; i++)

{

if (events[i].data.fd == listenfd)

{

peerlen = sizeof(peeraddr);

conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen);

if (conn == -1)

ERR_EXIT(“accept”);

printf(“ip=%s port=%d\n”, inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));

printf(“count = %d\n”, ++count);

clients.push_back(conn);

activate_nonblock(conn);

event.data.fd = conn;

event.events = EPOLLIN | EPOLLET;

epoll_ctl(epollfd, EPOLL_CTL_ADD, conn, &event);

}

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

{

conn = events[i].data.fd;

if (conn < 0)

continue;

char recvbuf[1024] = {0};

int ret = readline(conn, recvbuf, 1024);

if (ret == -1)

ERR_EXIT(“readline”);

if (ret == 0)

{

printf(“client close\n”);

close(conn);

event = events[i];

epoll_ctl(epollfd, EPOLL_CTL_DEL, conn, &event);

clients.erase(std::remove(clients.begin(), clients.end(), conn), clients.end());

}

fputs(recvbuf, stdout);

writen(conn, recvbuf, strlen(recvbuf));

}

}

}

return 0;

}

echocli.c

#include <unistd.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <signal.h>

#include <stdlib.h>

#include <stdio.h>

#include <errno.h>

#include <string.h>

#define ERR_EXIT(m) \

do \

{ \

perror(m); \

exit(EXIT_FAILURE); \

} while(0)

ssize_t readn(int fd, void *buf, size_t count)

{

size_t nleft = count;

ssize_t nread;

char *bufp = (char*)buf;

while (nleft > 0)

{

if ((nread = read(fd, bufp, nleft)) < 0)

{

if (errno == EINTR)

continue;

return -1;

}

else if (nread == 0)

return count – nleft;

bufp += nread;

nleft -= nread;

}

return count;

}

ssize_t writen(int fd, const void *buf, size_t count)

{

size_t nleft = count;

ssize_t nwritten;

char *bufp = (char*)buf;

while (nleft > 0)

{

if ((nwritten = write(fd, bufp, nleft)) < 0)

{

if (errno == EINTR)

continue;

return -1;

}

else if (nwritten == 0)

continue;

bufp += nwritten;

nleft -= nwritten;

}

return count;

}

ssize_t recv_peek(int sockfd, void *buf, size_t len)

{

while (1)

{

int ret = recv(sockfd, buf, len, MSG_PEEK);

if (ret == -1 && errno == EINTR)

continue;

return ret;

}

}

ssize_t readline(int sockfd, void *buf, size_t maxline)

{

int ret;

int nread;

char *bufp = buf;

int nleft = maxline;

while (1)

{

ret = recv_peek(sockfd, bufp, nleft);

if (ret < 0)

return ret;

else if (ret == 0)

return ret;

nread = ret;

int i;

for (i=0; i<nread; i++)

{

if (bufp[i] == ‘\n’)

{

ret = readn(sockfd, bufp, i+1);

if (ret != i+1)

exit(EXIT_FAILURE);

return ret;

}

}

if (nread > nleft)

exit(EXIT_FAILURE);

nleft -= nread;

ret = readn(sockfd, bufp, nread);

if (ret != nread)

exit(EXIT_FAILURE);

bufp += nread;

}

return -1;

}

void echo_cli(int sock)

{

/*

char sendbuf[1024] = {0};

char recvbuf[1024] = {0};

while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)

{

writen(sock, sendbuf, strlen(sendbuf));

int ret = readline(sock, recvbuf, sizeof(recvbuf));

if (ret == -1)

ERR_EXIT(“readline”);

else if (ret == 0)

{

printf(“client close\n”);

break;

}

fputs(recvbuf, stdout);

memset(sendbuf, 0, sizeof(sendbuf));

memset(recvbuf, 0, sizeof(recvbuf));

}

close(sock);

*/

fd_set rset;

FD_ZERO(&rset);

int nready;

int maxfd;

int fd_stdin = fileno(stdin);

if (fd_stdin > sock)

maxfd = fd_stdin;

else

maxfd = sock;

char sendbuf[1024] = {0};

char recvbuf[1024] = {0};

int stdineof = 0;

while (1)

{

if (stdineof == 0)

FD_SET(fd_stdin, &rset);

FD_SET(sock, &rset);

nready = select(maxfd+1, &rset, NULL, NULL, NULL);

if (nready == -1)

ERR_EXIT(“select”);

if (nready == 0)

continue;

if (FD_ISSET(sock, &rset))

{

int ret = readline(sock, recvbuf, sizeof(recvbuf));

if (ret == -1)

ERR_EXIT(“readline”);

else if (ret == 0)

{

printf(“server close\n”);

break;

}

fputs(recvbuf, stdout);

memset(recvbuf, 0, sizeof(recvbuf));

}

if (FD_ISSET(fd_stdin, &rset))

{

if (fgets(sendbuf, sizeof(sendbuf), stdin) == NULL)

{

stdineof = 1;

/*

close(sock);

sleep(5);

exit(EXIT_FAILURE);

*/

shutdown(sock, SHUT_WR);

}

else

{

writen(sock, sendbuf, strlen(sendbuf));

memset(sendbuf, 0, sizeof(sendbuf));

}

}

}

}

void handle_sigpipe(int sig)

{

printf(“recv a sig=%d\n”, sig);

}

int main(void)

{

/*

signal(SIGPIPE, handle_sigpipe);

*/

signal(SIGPIPE, SIG_IGN);

int sock;

if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)

ERR_EXIT(“socket”);

struct sockaddr_in servaddr;

memset(&servaddr, 0, sizeof(servaddr));

servaddr.sin_family = AF_INET;

servaddr.sin_port = htons(5188);

servaddr.sin_addr.s_addr = inet_addr(“127.0.0.1”);

if (connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)

ERR_EXIT(“connect”);

struct sockaddr_in localaddr;

socklen_t addrlen = sizeof(localaddr);

if (getsockname(sock, (struct sockaddr*)&localaddr, &addrlen) < 0)

ERR_EXIT(“getsockname”);

printf(“ip=%s port=%d\n”, inet_ntoa(localaddr.sin_addr), ntohs(localaddr.sin_port));

echo_cli(sock);

return 0;

}

makefile:

.PHONY:clean all

CC=gcc

CFLAGS=-Wall -g

BIN=echocli conntest pollsrv epollsrv

all:$(BIN)

%.o:%.c

$(CC) $(CFLAGS) -c $< -o $@

%.o:%.cpp

g++ $(CFLAGS) -c $< -o $@

epollsrv:epollsrv.o

g++ $(CFLAGS) $^ -o $@

clean:

rm -f *.o $(BIN)



版权声明:本文为fuyuehua22原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。