2

epoll鲜为人知的那点事work-in-progress

 3 years ago
source link: https://zhuanlan.zhihu.com/p/356846125
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

epoll鲜为人知的那点事work-in-progress

一个人NB的不是标签

epoll文章网络上已经有一大把了,包括知乎平台,但是我看到的只有索引文献里面的文章讲述了一个很基本却又重要的一个概念。

epoll监听的是file description,而不是file descriptor。

我们都知道file descriptor,也就是文件描述符。但是什么是file description呢?只有理解了file description才能理解epoll深藏在表面下面的一些坑。

v2-2d031343dde498a714d022ffb3a01823_720w.jpg

图片来源于:https://miro.medium.com/max/546/1*ObWegZ_IDTqGVH2KLYxPSA.png

如果英文阅读能力没问题,直接阅读图片出处即可明白epoll是如何工作的,什么是file description.

file descriptor,文件描述符,我们经常会用到,当我们打开一个文件,或者监听一个端口的时候,系统会返回一个文件描述符。接着我们的操作都是针对文件描述符进行,比如下面的C++代码监听一个端口

#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <thread>
void accept_print(int sockfd);
void print_by_tid(std::string c)
{
   std::cout<<std::this_thread::get_id() << ": " << c << std::endl;
}
void print_by_tid(std::string c, unsigned int s) {
      std::cout<<std::this_thread::get_id() << ": " << c << "\t" << s <<std::endl;

}

int main(int argc, char *argv[])
{
   int portno;
   struct sockaddr_in serv_addr;

   /* First call to socket() function */
   int sockfd = socket(AF_INET, SOCK_STREAM, 0);  //返回文件描述符

   if (sockfd < 0)
   {
      perror("ERROR opening socket");
      exit(1);
   }

   /* Initialize socket structure */
   bzero((char *)&serv_addr, sizeof(serv_addr));
   portno = 5001;

   serv_addr.sin_family = AF_INET;
   serv_addr.sin_addr.s_addr = INADDR_ANY;
   serv_addr.sin_port = htons(portno);

   /* Now bind the host address using bind() call.*/
   if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
   {
      perror("ERROR on binding");
      exit(1);
   }

   /* Now start listening for the clients, here process will
      * go in sleep mode and will wait for the incoming connection
   */

   listen(sockfd, 5);

   int read_fd_count = 1;

      struct timeval timeout;
      timeout.tv_sec = 20;
      timeout.tv_usec = 0;
      fd_set temp_hs;
      FD_ZERO(&temp_hs);
      FD_SET(sockfd, &temp_hs);

      int num_ready = select(FD_SETSIZE, &temp_hs, nullptr, nullptr, &timeout);
      if (num_ready < 0)
      {
         perror("error in select()");
      }
      else if (num_ready == 0)
      {
         print_by_tid("timeout\n");
      }
      else
      {
         for (int i = 0; i < read_fd_count; i++)
            if (FD_ISSET(sockfd, &temp_hs))
            {
               print_by_tid("fd %d is ready for reading", sockfd);
               accept_print(sockfd);
            }
            else
            {
               print_by_tid("fd %d is not ready for reading though select is wake up", sockfd);
            }
            
      }

}

void accept_print(int sockfd)
{
   int cli_addr;
   unsigned int clilen = sizeof(cli_addr);
   /* Accept actual connection from the client */
   print_by_tid("accepting socket");
   int newsockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen);
   print_by_tid("accepted socket");
   if (newsockfd < 0)
   {
      perror("ERROR on accept");
      exit(1);
   }
   char buffer[256];

   /* If connection is established then start communicating */
   bzero(buffer, 256);
   int n = read(newsockfd, buffer, 3);
   printf("received bytes: %s", buffer);
   n = read(newsockfd, buffer, 252);
   printf("received bytes: %s", buffer+4);

   if (n < 0)
   {
      perror("ERROR reading from socket");
      exit(1);
   }
   std::string s = std::string("Here is the message: ").append(buffer);
   print_by_tid(s);

   /* Write a response to the client */
   //n = write(newsockfd, "I got your message", 18);

   if (n < 0)
   {
      perror("ERROR writing to socket");
      exit(1);
   }
}

而什么是file description呢?且翻译为文件描述,比文件描述符少一个“符”。

文件描述是操作系统的一个数据结构用来记录文件被操作的信息,而文件描述符就只是进程里面指向文件描述的数字。比如某个文件描述记录了你对这个文件读取了多少个内容,现在的file offset在哪里。(操作系统会维护一个open file table,用于记录所有的文件描述。)

不同的文件描述会指向同一个文件,不同的文件描述符可以指向同一个文件描述。

注:图片都是来源于索引第三篇文章。

References:

https://en.wikipedia.org/wiki/Epoll​en.wikipedia.orgEpoll is fundamentally broken 2/2​idea.popcount.orghttps://medium.com/@copyconstruct/the-method-to-epolls-madness-d9d2d6378642​medium.com


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK