Linux IO 复用之 epoll 介绍与 epoll 应用(编写单线程多并发的 Web 服务器)

一、Linux epoll 介绍

epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率(百度百科)。

  • epoll

    • The epoll API performs a similar task to poll: monitoring multiple file descriptors to see if I/O is possible on any of them. The epoll API can be used either as an edge-triggered or a level-triggered interface and scales well to large numbers of watched file descriptors.

      epoll 执行与 poll 相似的任务:通过监控多个文件描述符来查看任何一个(被监控的)文件描述符是否可能有IO操作。epoll API 要么通过edge-triggered要么通过level-triggered接口来使用并且可以很好地扩展到大量被监控的文件描述符(可用于实现Web服务器的原因)。

  • System calls

    • epoll_create(): creates an epoll instance and returns a file descriptor referring to that instance(创建一个epoll实例并且返回一个指代该实例的文件描述符).

      • int epoll_create(int size)
        Linux IO 复用之 epoll 介绍与 epoll 应用(编写单线程多并发的 Web 服务器)_第1张图片
    • epoll_ctl(): Interest in particular file descriptors is then registered via epoll_ctl. The set of file descriptors currently registered on an epoll instance is sometimes called an epoll set.

      感兴趣的特定文件描述符通过epoll_ctl注册到 epoll 实例。
      当前注册到一个 epoll 实例的文件描述符集合有时被称作 epoll 集合。

      • int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
        • epfd: epoll 文件描述符
        • op: 通过指定op来添加(EPOLL_CTL_ADD)/修改(EPOLL_CTL_MOD)/删除(EPOLL_CTL_DEL)需要侦听的文件描述符及其事件
        • fd: 文件描述符
        • event: 链接到文件描述符的事件,struct epoll_event 定义如下
          Linux IO 复用之 epoll 介绍与 epoll 应用(编写单线程多并发的 Web 服务器)_第2张图片
    • epoll_wait: waits for I/O events(等待IO事件)

      • int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
        • epfd: epoll 文件描述符
        • events: The memory area pointed to by events will contain the events that will be available for the caller.
        • maxevents: epoll_wait 返回的最大事件数量
        • timeout: timeout为0的时候表示马上返回,为-1的时候表示一直等下去,直到有事件发生,为正整数(t)的时候表示等待t毫秒。
          Linux IO 复用之 epoll 介绍与 epoll 应用(编写单线程多并发的 Web 服务器)_第3张图片

二、单线程多并发的 Web 服务器编写

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define MAX_EVENTS 10

// 处理get请求
void handle_get(int fd, char path[]){
     
        char buf[1024];
        int n;
        int filefd;
        if((filefd=open(path+1, O_RDONLY))==-1){
     
                write(fd, "HTTP/1.0 404 Not Found\r\n\r\n", 26);
                return;
        }
        write(fd, "HTTP/1.0 200 OK\r\n\r\n", 19);
        while((n=read(filefd, buf, sizeof(buf)))>0){
     
                write(fd, buf, n);
        }
        close(filefd);
}

// 非阻塞设置
void setnonblocking(int fd) {
     
        int opts;
        opts=fcntl(fd, F_GETFL);
        if(opts<0) {
     
                perror("fcntl(sock,GETFL)");
                exit(1);
        }
        opts = opts|O_NONBLOCK;
        if(fcntl(fd, F_SETFL, opts)<0) {
     
                perror("fcntl(sock,SETFL,opts)");
                exit(1);
        }
}


main(int ac, char *av[]){
     
        struct sockaddr_in addr;
        char buf[1024];
        int i;
        int n;
        char cmd[512];
        char path[512];
        int sockfd;

        if(ac<2){
     
                printf("Usage cmd port_num\n");
                exit(1);
        }

		struct epoll_event ev, events[MAX_EVENTS];
        int listen_sock, conn_sock, nfds, epollfd;

        signal(SIGPIPE, SIG_IGN);

        listen_sock = socket(AF_INET, SOCK_STREAM, 0);

        addr.sin_family = AF_INET;
        addr.sin_port = htons(atoi(av[1]));
        addr.sin_addr.s_addr = INADDR_ANY;
		
        if(bind(listen_sock, (const struct sockaddr *)&addr, sizeof(struct sockaddr_in))==-1){
     
                perror("cannot bind");
                exit(1);
        }
		
        listen(listen_sock, 1);
		// 创建 epoll 实例
        epollfd = epoll_create(10);
        if (epollfd == -1) {
     
                perror("epoll_create");
                exit(EXIT_FAILURE);
        }
        // 注册 socket 文件描述符
        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);
        }
		
        while(1){
     
        		
                nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
                if (nfds == -1) {
     
                        perror("epoll_pwait");
                        exit(EXIT_FAILURE);
                }
				for (n = 0; n < nfds; ++n) {
     
						// 主socket通过accept获取客户端与服务器之间的socket文件描述符并注册到epoll实例
                        if (events[n].data.fd == listen_sock) {
     
                        		// 注册客户端与服务器之间的socket文件描述符
                                conn_sock = accept(listen_sock, NULL, NULL);
                                if (conn_sock == -1) {
     
                                        perror("accept");
                                        exit(EXIT_FAILURE);
                                }
                                setnonblocking(conn_sock);
                                ev.events = EPOLLIN | EPOLLET; // 处理的事件类型为EPOLLIN
                                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 {
      // 客户端与服务器之间的socket文件描述符用于读取客户端GET请求(EPOLLIN)
                                sockfd=events[n].data.fd;
                                if ((i = read(sockfd, buf, sizeof(buf))) <= 0) {
     
                                        close(sockfd);
                                } else {
     
                                        sscanf(buf, "%s%s", cmd, path);
                                        if(strcmp(cmd, "GET")==0){
     
                                                handle_get(sockfd, path);
                                        }
                                        close(sockfd);  //close the sockfd
                                }
                        }
                }
		}
}

你可能感兴趣的:(Linux,linux,服务器)