您的位置:首页 > 运维架构 > Linux

select与epoll,多路IO复用

2011-09-24 22:04 471 查看
当一个节点建立了与多个节点之间的连接时,如何去高效的处理多个连接的数据呢?

可能有的连接有数据,有的连接没有数据,这中情况下,我们总不能一个个的去recv尝试吧? linux下面给我们提供了select和epoll两组接口以处理这种情况。

1 select:

#include <sys/select.h>

      #include <sys/time.h>

       #include <sys/types.h>

       #include <unistd.h>

 int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

       void FD_CLR(int fd, fd_set *set);

       int  FD_ISSET(int fd, fd_set *set);

       void FD_SET(int fd, fd_set *set);

       void FD_ZERO(fd_set *set);

select使用时,有三个集合: readfds,writefds,exceptfds集合,每次select会将得到的有对应事件文件描述符放在这三个集合中。调用者要自行判断文件描述符(或者socket)是否在对应的集合中。每次判定前,都要清空要传入的集合。nfds是需要监管的描述符的最大值+1.比这个值更大的描述符上如果有事件发生不会被监管到。

2 poll:

       #include <sys/epoll.h>

struct pollfd {

int fd; /* file descriptor */

short events; /* requested events to watch */

short revents; /* returned events witnessed */

};

int poll (struct pollfd *fds, unsigned int nfds, int timeout);

poll的接口是每次传递一个需要监听的集合,集合中的每个对象包含了要监听的文件描述符,要监听的事件,已经最终得到的事件。

成功时,poll()返回结构体中revents域不为0的文件描述符个数;如果在超时前没有任何事件发生,poll()返回0;失败时,poll()返回-1,并设置errno。

3 epoll:

 #include <sys/epoll.h>

 int epoll_create(int size);

 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);

  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 */

           };

epoll的使用,首先通过epoll_create创建一个epoll对象,得到一个epoll的描述符,我们需要监听哪个fd上的事件通过epoll_ctl,最后 epoll_wait会返回发生了时间的数目,具体的时间在参数events中。

 #define MAX_EVENTS 10

           struct epoll_event ev, events[MAX_EVENTS];

           int listen_sock, conn_sock, nfds, epollfd;

           /* Set up listening socket, 'listen_sock' (socket(),

              bind(), listen()) */

           epollfd = epoll_create(10);

           if (epollfd == -1) {

               perror("epoll_create");

               exit(EXIT_FAILURE);

           }

           ev.events = EPOLLIN;

           ev.data.fd = listen_sock;

           if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {

               perror("epoll_ctl: listen_sock");

               exit(EXIT_FAILURE);

           }

           for (;;) {

               nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);

               if (nfds == -1) {

                   perror("epoll_pwait");

                   exit(EXIT_FAILURE);

               }

               for (n = 0; n < nfds; ++n) {

                   if (events
.data.fd == listen_sock) {

                       conn_sock = accept(listen_sock,

                                       (struct sockaddr *) &local, &addrlen);

                       if (conn_sock == -1) {

                           perror("accept");

                           exit(EXIT_FAILURE);

                       }

}

 for (;;) {

               nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);

               if (nfds == -1) {

                   perror("epoll_pwait");

                   exit(EXIT_FAILURE);

               }

               for (n = 0; n < nfds; ++n) {

                   if (events
.data.fd == listen_sock) {

                       conn_sock = accept(listen_sock,

                                       (struct sockaddr *) &local, &addrlen);

                       if (conn_sock == -1) {

                           perror("accept");

                           exit(EXIT_FAILURE);

                       }

                       setnonblocking(conn_sock);

                       ev.events = EPOLLIN | EPOLLET;

                       ev.data.fd = conn_sock;

                       if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,

                                   &ev) == -1) {

                           perror("epoll_ctl: conn_sock);

                           exit(EXIT_FAILURE);

                       }

                   } else {

                       do_use_fd(events
.data.fd);

                   }

               }

           }

4 对比:

select使用时,FD_SET是一个静态变量,其最大值由FD_SETSIZE设置,对于每个进程默认2048,只能通过开启多个进程或者重新编译内核解决该问题。

poll所支持的fd是一个进程能够打开的文件描述符。

select和poll,每次都会线性的判断fd集合中哪些发生了事件,并将该事件记录下来。如果同时建立的连接很多,但是只有少数的有事件发生,这种情况下,效率就会很低。epoll的实现中就避免了该问题,对于要监管的每个fd都会有回调函数,当该fd上发生事件时,会调用对应的回调函数。这样,在连接很多,少数事件发生的情况下,依旧会效率很高。当然,如果连接很多,大部分连接都有事件时,两者的效率应该是差不多的。

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息