基于 epoll 实现 web 服务器

1. 简介
  
  epoll 是 Linux 平台下特有的一种 I/O 复用模型实现,于 2002 年在 Linux kernel 2.5.44 中被引入。在 epoll 之前,Unix/Linux 平台下的 I/O 复用模型包含 select 和 poll 两个系统调用。随着因特网的发展,因特网的用户量越来越大,C10K 问题出现。基于 select 和 poll 编写的网络服务已经不能满足不能满足用户的需求了,业界迫切希望更高效的系统调用出现。在此背景下,FreeBSD 的 kqueue 和 Linux 的 epoll 被研发了出来。kqueue 和 epoll 的出现,终结了 C10K 问题,C10K 问题就此作古。
  
  因为 Linux 系统的广泛应用,所以大家在说 I/O 复用时,更多的是想到了 epoll,而不是 kqueue,本文也不例外。本篇文章不会涉及 kqueue,大家有兴趣可以自己看看。
  
  2. 基于 epoll 实现 web 服务器
  
  在 Linux 中,epoll 并不是一个系统调用,而是 epoll_create、epoll_ctl 和 epoll_wait 三个系统调用的统称。关于这三个系统调用的细节,这里就不说明了,大家可以自己去查 man-page。接下来,我们来直接看一个例子,这个例子基于 epoll 和 TinyHttpd 实现了一个 I/O 复用版的 HTTP Server。在上代码前,我们先来演示这个玩具版 HTTP Server 的效果。
  
  上面就是玩具版 HTTP Server 的运行效果了,看起来还行。在我第一次把它成功跑起来的时候,感觉很奇妙。好了,看完效果,接下来看代码吧,如下:
  
  #include
  
  #include
  
  #include
  
  #include
  
  #include
  
  #include
  
  #include
  
  #include
  
  #include
  
  #include
  
  #include
  
  #include
  
  #include
  
  #include "httpd.h"
  
  #define DEFAULT_PORT 8080
  
  #define MAX_EVENT_NUM 1024
  
  #define INFTIM -1
  
  void process(int);
  
  void handle_subprocess_exit(int);
  
  int main(int argc, char *argv[])
  
  {
  
  struct sockaddr_in server_addr;
  
  int listen_fd;
  
  int cpu_core_num;
  
  int on = 1;
  
  listen_fd = socket(AF_INET, SOCK_STREAM, 0);
  
  fcntl(listen_fd, F_SETFL, O_NONBLOCK);    // 设置 listen_fd 为非阻塞
  
  setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
  
  bzero(&server_addr, sizeof(server_addr));
  
  server_addr.sin_family = AF_INET;
  
  server_addr.sin_addr.s_addr = www.douniu178.com  htonl(INADDR_ANY);
  
  server_addr.sin_port = htons(DEFAULT_PORT);
  
  if (bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
  
  perror("bind error, message: ");
  
  exit(1);
  
  }
  
  if (listen(listen_fd, www.chushiyl.cn  5) == -1) {
  
  perror("listen error, message: ");
  
  exit(1);
  
  }
  
  printf("listening 8080\n");
  
  signal(SIGCHLD, handle_subprocess_exit);
  
  cpu_core_num = get_nprocs();
  
  printf("cpu core num: %d\n", www.ahor.net cpu_core_num);
  
  // 根据 CPU 数量创建子进程,为了演示“惊群现象”,这里多创建一些子进程
  
  for (int i = 0; i < cpu_www.douniu828.com core_num * 2; i++) {
  
  pid_t pid = fork();
  
  if (pid == 0) {    // 子进程执行此条件分支
  
  process(listen_fd);
  
  exit(0);
  
  }
  
  }
  
  while (1) {
  
  sleep(1);
  
  }
  
  return 0;
  
  }
  
  void process(int listen_fd)
  
  {
  
  int conn_fd;
  
  int ready_fd_num;
  
  struct sockaddr_in client_addr;
  
  int client_addr_size = sizeof(client_addr);
  
  char buf[128];
  
  struct epoll_event ev, events[MAX_EVENT_NUM];
  
  // 创建 epoll 实例,并返回 epoll 文件描述符
  
  int epoll_fd = epoll_create(MAX_EVENT_NUM);
  
  ev.data.fd = listen_fd;
  
  ev.events = EPOLLIN;
  
  // 将 listen_fd 注册到刚刚创建的 epoll 中
  
  if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &ev) == -1) {
  
  perror("epoll_ctl error, message: ");
  
  exit(1);
  
  }
  
  while(1) {
  
  // 等待事件发生
  
  ready_fd_num = epoll_wait(epoll_fd, events, MAX_EVENT_NUM, INFTIM);
  
  printf("[pid %d]

你可能感兴趣的:(基于 epoll 实现 web 服务器)