Linux API/linux

select, poll, epoll

구차니 2021. 11. 2. 10:44

개략적으로 보면.. select나 poll이나 비슷하게 이벤트 발생시 누가 발생했는지 찾아서 처리해야 하고

epoll은 이벤트 발생시 발생목록을 전달하여, 누가 발생했는지 찾을 필요 없이 바로 처리하면 된다.

다만 select는 1024가 한번에 다룰수 있는 최대 fd 갯수고

poll은 숫자제한은 없지만 선형 탐색을 해야하고, 1500 넘으면 느려진다고 하고

epoll이 1500 보다 클 경우는 유리한 듯.

 

select / poll / epoll

[링크 : https://niklasjang.github.io/backend/select-poll-epoll/]

[링크 : https://applefarm.tistory.com/144]

 

select

while(1)
{
   fd_num = select(...);
   if(fd_num == -1) break;
   else if(fd_num == 0 ) continue;

   for(int fd =0; fd < fd_max + 1; ++fd)
   {
      if(FD_ISSET(fd, &cpy_reads))
      {
          // TODO
      }
   }

   close(server_socket);
   return 0;
}

[링크 : https://ozt88.tistory.com/21]

 

poll

       #include <poll.h>
       #include <fcntl.h>
       #include <sys/types.h>
       #include <stdio.h>
       #include <stdlib.h>
       #include <unistd.h>

       #define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                               } while (0)

       int
       main(int argc, char *argv[])
       {
           int nfds, num_open_fds;
           struct pollfd *pfds;

           if (argc < 2) {
              fprintf(stderr, "Usage: %s file...\n", argv[0]);
              exit(EXIT_FAILURE);
           }

           num_open_fds = nfds = argc - 1;
           pfds = calloc(nfds, sizeof(struct pollfd));
           if (pfds == NULL)
               errExit("malloc");

           /* Open each file on command line, and add it 'pfds' array. */

           for (int j = 0; j < nfds; j++) {
               pfds[j].fd = open(argv[j + 1], O_RDONLY);
               if (pfds[j].fd == -1)
                   errExit("open");

               printf("Opened \"%s\" on fd %d\n", argv[j + 1], pfds[j].fd);

               pfds[j].events = POLLIN;
           }

           /* Keep calling poll() as long as at least one file descriptor is
              open. */

           while (num_open_fds > 0) {
               int ready;

               printf("About to poll()\n");
               ready = poll(pfds, nfds, -1);
               if (ready == -1)
                   errExit("poll");

               printf("Ready: %d\n", ready);

               /* Deal with array returned by poll(). */

               for (int j = 0; j < nfds; j++) {
                   char buf[10];

                   if (pfds[j].revents != 0) {
                       printf("  fd=%d; events: %s%s%s\n", pfds[j].fd,
                               (pfds[j].revents & POLLIN)  ? "POLLIN "  : "",
                               (pfds[j].revents & POLLHUP) ? "POLLHUP " : "",
                               (pfds[j].revents & POLLERR) ? "POLLERR " : "");

                       if (pfds[j].revents & POLLIN) {
                           ssize_t s = read(pfds[j].fd, buf, sizeof(buf));
                           if (s == -1)
                               errExit("read");
                           printf("    read %zd bytes: %.*s\n",
                                   s, (int) s, buf);
                       } else {                /* POLLERR | POLLHUP */
                           printf("    closing fd %d\n", pfds[j].fd);
                           if (close(pfds[j].fd) == -1)
                               errExit("close");
                           num_open_fds--;
                       }
                   }
               }
           }

           printf("All file descriptors closed; bye\n");
           exit(EXIT_SUCCESS);
       }

[링크 : https://man7.org/linux/man-pages/man2/poll.2.html]

 

epoll

int epoll_fd = epoll_create(EPOLL_SIZE);
struct epoll_event* events = malloc(sizeof(struct epoll_event)*EPOLL_SIZE);
struct epoll_event init_event;
init_event.events = EPOLLIN;
init_event.data.fd = server_socket;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_socket, &init_event);
while(TRUE)
{
   int event_count = epoll_wait(epoll_fd, events, EPOLL_SIZE, -1);
   if( event_count = -1 ) break;
   for( int i = 0 ; i < event_count; ++i )
   {
      if(events[i].data.fd == server_socket) //서버 소켓에 이벤트
      { //accept 처리 ... init_event.events = EPOLLIN;
         init_event.data.fd = new_client_socket;
         epoll_ctl(epoll_fd, EPOLL_CTL_ADD, new_client_socket, &init_event);
      }
      else //이벤트가 도착한 소켓들
      {
            //read, write, closesocket처리
      }
   }
}

closesocket(server_socket);
close(epoll_fd);
return 0;

[링크 : https://ozt88.tistory.com/21]

 

 

 

+

오래된 글이지만 벤치마크 결과 poll/select는 거의 동일한 성능이고

fd 갯수가 늘어갈수록 느려지지만, epoll이나 kqueue의 경우 일정 상한 이상으로는 성능이 유지되는 경향을 보인다.

[링크 : https://monkey.org/~provos/libevent/libevent-benchmark.jpg]

[링크 : https://kldp.org/node/46542]

 

일반적으로 소켓수가 1500이하일때는 퍼모먼스 차이는 거의 없는것으로 알고 있습니다.

select poll epoll 의 성능차이는 5000 이상부터 급격히 달라집니다.

[링크 : http://foroum.gpgstudy.com/forum/viewtopic.php?t=12610]